C ++ 17 inline
biến
Nếu bạn đã tìm kiếm "C ++ const static", thì đây rất có thể là thứ bạn thực sự muốn sử dụng là các biến nội tuyến của C ++ 17 .
Tính năng C ++ 17 tuyệt vời này cho phép chúng tôi:
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
không phải thế
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
không chính xác
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Biên dịch và chạy:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
GitHub ngược dòng .
Xem thêm: Làm thế nào để các biến nội tuyến hoạt động?
Tiêu chuẩn C ++ về các biến nội tuyến
Tiêu chuẩn C ++ đảm bảo rằng các địa chỉ sẽ giống nhau. Dự thảo tiêu chuẩn C ++ 17 N4659
10.1.6 "Trình xác định nội tuyến":
6 Hàm nội tuyến hoặc biến có liên kết ngoài sẽ có cùng địa chỉ trong tất cả các đơn vị dịch.
cppreference https://en.cppreference.com/w/cpp/lingu/inline giải thích rằng nếu static
không được đưa ra, thì nó có liên kết bên ngoài.
Thực hiện biến nội tuyến GCC
Chúng ta có thể quan sát cách nó được thực hiện với:
nm main.o notmain.o
trong đó có:
main.o:
U _GLOBAL_OFFSET_TABLE_
U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i
notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
và man nm
nói về u
:
"u" Biểu tượng là một biểu tượng toàn cầu độc đáo. Đây là một phần mở rộng GNU cho tập hợp các ràng buộc ký hiệu ELF tiêu chuẩn. Đối với một biểu tượng như vậy, trình liên kết động sẽ đảm bảo rằng trong toàn bộ quá trình chỉ có một biểu tượng có tên và loại được sử dụng.
vì vậy chúng tôi thấy rằng có một phần mở rộng ELF dành riêng cho việc này.
Tiền C ++ 17: extern const
Trước C ++ 17 và trong C, chúng ta có thể đạt được hiệu ứng rất giống với extern const
, điều này sẽ dẫn đến một vị trí bộ nhớ duy nhất được sử dụng.
Nhược điểm trên inline
là:
- không thể thực hiện biến
constexpr
với kỹ thuật này, chỉ inline
cho phép: Làm thế nào để khai báo constexpr extern?
- nó ít thanh lịch hơn khi bạn phải khai báo và định nghĩa biến riêng trong tệp tiêu đề và cpp
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
không chính xác
#include "notmain.hpp"
const int notmain_i = 42;
const int* notmain_func() {
return ¬main_i;
}
không phải thế
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
extern const int notmain_i;
const int* notmain_func();
#endif
GitHub ngược dòng .
Tiêu đề thay thế Pre-C ++ 17
Đây không phải là extern
giải pháp tốt, nhưng chúng hoạt động và chỉ chiếm một vị trí bộ nhớ duy nhất:
Một constexpr
hàm, bởi vì constexpr
ngụ ýinline
và inline
cho phép (buộc) định nghĩa xuất hiện trên mọi đơn vị dịch thuật :
constexpr int shared_inline_constexpr() { return 42; }
và tôi cá rằng bất kỳ trình biên dịch tử tế nào cũng sẽ thực hiện cuộc gọi.
Bạn cũng có thể sử dụng một const
hoặc constexpr
biến tĩnh như trong:
#include <iostream>
struct MyClass {
static constexpr int i = 42;
};
int main() {
std::cout << MyClass::i << std::endl;
// undefined reference to `MyClass::i'
//std::cout << &MyClass::i << std::endl;
}
nhưng bạn không thể làm những việc như lấy địa chỉ của nó, nếu không nó sẽ trở nên khó sử dụng, xem thêm: Xác định các thành viên dữ liệu tĩnh constexpr
C
Trong C, tình huống giống như C ++ trước C ++ 17, tôi đã tải lên một ví dụ tại: "static" nghĩa là gì trong C?
Sự khác biệt duy nhất là trong C ++, const
ngụ ý static
cho toàn cầu, nhưng nó không có trong ngữ nghĩa C: C ++ của `static const` vs` const`
Bất kỳ cách nào để hoàn toàn nội tuyến nó?
TODO: có cách nào để hoàn toàn nội tuyến biến, mà không sử dụng bất kỳ bộ nhớ nào không?
Giống như những gì bộ tiền xử lý làm.
Điều này sẽ yêu cầu bằng cách nào đó:
- cấm hoặc phát hiện nếu địa chỉ của biến được lấy
- thêm thông tin đó vào các tệp đối tượng ELF và để LTO tối ưu hóa nó lên
Liên quan:
Đã thử nghiệm trong Ubuntu 18.10, GCC 8.2.0.