2015-01-25 14 views
5

Tôi có đoạn mã sau:constexpr của thành viên lớp tuple tĩnh có mối liên kết lỗi

#include <iostream> 
#include <tuple> 

class T 
{ 
    public: 
     using Names = std::tuple<char const*, char const*>; 
     static constexpr Names names {"First", "Second"}; 
}; 

int main() 
{ 
    std::cout << std::get<0>(T::names); 
} 

Như names là một constexpr tôi mong đợi này để làm việc. Nhưng tôi nhận được một lỗi mối liên kết:

Trình biên dịch:

> g++ --version 
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn) 
Target: x86_64-apple-darwin14.0.0 
Thread model: posix 

Lỗi:

> g++ -std=c++1y pl.cpp 
Undefined symbols for architecture x86_64: 
    "T::names", referenced from: 
     _main in pl-377031.o 
ld: symbol(s) not found for architecture x86_64 
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

[live demo]

Trả lời

7

Một tuyên bố của một thành viên static dữ liệu trong lớp học không bao giờ là một định nghĩa .
Định nghĩa là cần thiết bất cứ khi nào một biến được sử dụng không được sử dụng . std::get<> nhận đối số cho mỗi tham chiếu và liên kết biến với tham chiếu odr-sử dụng ngay lập tức .

Đơn giản chỉ cần xác định names bên ngoài:

constexpr T::Names T::names; // Edit: This goes *outside* the class "as is"! 

Demo.


1) [basic.def]/2:

A declaration is a definition unless [..] it declares a static data member in a class definition (9.2, 9.4)

2) [basic.def.odr]/4:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

3) Theo [basic.def.odr]/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e , where either the lvalue-to-rvalue conversion (4.1) is applied to e , or e is a discarded-value expression (Clause 5).

Tại đây, biểu thức id T::names đề cập đến biến được đề cập. Sự thừa nhận duy nhất e có chứa tất cả các kết quả tiềm năng của T::names chính là T::names, bởi vì tập hợp các kết quả tiềm năng của một cuộc gọi hàm, tức là std::get<0>(T::names), trống. Tuy nhiên, chuyển đổi lvalue-to-rvalue rõ ràng là không được áp dụng, và giá trị của T::names cũng rõ ràng không bị loại bỏ (khi nó được chuyển đến một hàm).
Vì vậy, nó được sử dụng không đúng và yêu cầu một định nghĩa.

+0

Tôi giả định rằng 'constexpr T :: Tên T :: tên;' phải đi trong đơn vị biên dịch và không thể nằm trong tệp tiêu đề được bao gồm nhiều lần. –

+0

@CrappyExperienceBye Đúng - IIRC 'T :: names' có liên kết bên ngoài. – Columbo

+0

Tôi có một thời gian khó hiểu việc sử dụng odr vì thuật ngữ liên quan: Tôi đã đọc về các biểu thức có khả năng đánh giá, chuyển đổi từ rvalue sang rvalue, và các kết quả tiềm năng trong Standard và cppreference nhưng nó khó hiểu. Có một nguồn toàn diện giải thích về sử dụng tiếng Anh không? –

2

@Columbo đăng giải pháp đúng.

Rất tiếc, tôi đang cố xây dựng thư viện chỉ tiêu đề. Giải pháp yêu cầu thành viên tĩnh được biên dịch thành một đơn vị biên dịch (đó là những gì tôi đang sử dụng constexpr với hy vọng tránh).

Vì vậy, tôi cần phải thực hiện một bước ngoặt khác vào các tác phẩm để làm việc hiệu quả.Đây chỉ là để chia sẻ giải pháp của tôi:

#include <iostream> 

class T 
{ 
    public: 
     using Names = std::tuple<char const*, char const*>; 
     template<std::size_t index> 
     static char const* getName() 
     { 
      static constexpr Names names {"First", "Second"}; 
      return std::get<index>(names); 
     } 
}; 

int main() 
{ 
    std::cout << T::getName<0>() << "\n"; 
} 
Các vấn đề liên quan