2016-10-24 16 views
16

Trong một cuộc thảo luận về câu hỏi khác, tôi đã được đưa ra một ví dụ nơi dường như mối liên hệ của một định ảnh hưởng khả năng sử dụng của nó trong một biểu thức hằng:Tại sao các biến liên kết bên ngoài có thể sử dụng dưới dạng biểu thức không đổi?

extern char const a[] = "Alpha"; 
char constexpr b[] = "Beta"; 
char const g[] = "Gamma"; 

template <const char *> void foo() {} 

auto main() 
    -> int 
{ 
    foo<a>();  // Compiles 
    foo<b>();  // Compiles 
    foo<g>();  // Doesn't compile 
} 

Các lỗi từ cuối cùng (với GCC) là:

test.cc: In function 'int main()': 
test.cc:12:13: error: the value of 'g' is not usable in a constant expression 
     foo<g>();  // Doesn't compile 
      ^
test.cc:3:16: note: 'g' was not declared 'constexpr' 
    char const g[] = "Gamma"; 
       ^

Tôi có thể đã bỏ lỡ tầm quan trọng của ví dụ trong cuộc thảo luận trước đó, bởi vì tôi tin rằng nó không thể chỉ là mối liên kết khác biệt foo<a> từ foo<g> - tuy nhiên, tôi đã bắt đầu nghi ngờ vị trí đó.

  1. Đây có phải là liên kết hay một số thuộc tính khác được cấp bởi extern, cho phép foo<a>()?
  2. Lý do cho phép foo<a>() nhưng không phải là foo<g>() là gì? Cụ thể, nếu nó được xác định bởi liên kết, tại sao liên kết bên trong sẽ làm cho biến không thể sử dụng được dưới dạng biểu thức liên tục khi cùng một biến được khai báo externsẽ có thể sử dụng được không?
  3. Người ta cho rằng câu hỏi của biểu tượng đang hiển thị (hoặc không) đối với mối liên kết có liên quan ở đây. Với tôi, có vẻ như thực tế biến thể foo<b> vẫn được cho phép ngay cả khi static được thêm vào sẽ loại bỏ điều này - hoặc tôi có nhầm lẫn không?
  4. (Sự khác biệt giữa foo<b>()foo<g>() được bao phủ đầy đủ bởi other questions, tôi nghĩ).
+1

Clang vui vẻ chấp nhận cả ba. –

+0

@ T.C. nó cũng vậy ... chết tiệt. Bất kỳ ý tưởng mà trình biên dịch là chính xác? – davmac

+0

GCC 6 cũng chấp nhận với '-std = C++ 1z'. –

Trả lời

6

Lỗi GCC.

N3337 (đó là C++ 11 + biên tập sửa) [temp.arg.nontype]/2 có một ví dụ đó là trực tiếp trên điểm:

template<class T, const char* p> class X { 
    /* ... */ 
}; 
X<int, "Studebaker"> x1; // error: string literal as template-argument 

const char p[] = "Vivisectionist"; 
X<int,p> x2; // OK 

Trong C++ template lập luận 03 tài liệu tham khảo/con trỏ được giới hạn ở những thứ có liên kết bên ngoài, nhưng hạn chế đó đã bị loại bỏ trong C++ 11.

Quy tắc tham chiếu mẫu/tham chiếu mẫu được giải phóng trong C++ 17 để cho phép tất cả các biểu thức liên tục, vì vậy có lẽ lý do GCC chấp nhận ví dụ với -std=c++1z là nó đi qua một đường dẫn mã khác trong chế độ đó.

+0

Chapeau. Thật sự thú vị. +1 – skypjack

3

Đây là một sự trùng hợp kỳ lạ. Tôi chỉ đọc về điều này trong C++ Templates chỉ tối qua. Khi sử dụng con trỏ làm tham số không nhập mẫu, đó là địa chỉ được chứa trong con trỏ chứ không phải giá trị được trỏ tới bởi con trỏ là hằng số được thay thế làm đối số mẫu. Do đó, địa chỉ phải có thể biết được tại thời gian biên dịch và duy nhất trên tất cả các đơn vị biên dịch để tránh vi phạm ODR. Điều này đúng với các biến số constexprextern, nhưng không đúng với các tệp có liên kết toàn cầu hoặc tệp. Đây là một ví dụ.

static char const foo[] = "Hello"; 
char const bar[] = "Hello"; 
constexpr char const baz[] = "Hello"; 
extern char const qux[] = "Hello"; 

template <char const*> 
struct my_struct{}; 

int main() { 
    my_struct<foo> f;  // ERROR: Address is unique, but not known until runtime 
    my_struct<bar> b;  // ERROR: Address may or may not be unique (ODR violation) and not known until runtime 
    my_struct<baz> bz;  // OK: constexpr 
    my_struct<qux> q;  // OK: extern 
} 
+0

Điều này không đúng vì C++ 11 đã loại bỏ giới hạn liên kết. –

+1

Trong tất cả bốn ví dụ, trình liên kết quyết định địa chỉ của các biến. Trình biên dịch không bao giờ biết các đơn vị biên dịch khác sẽ được trộn lẫn với nhau để tạo ra tệp thực thi cuối cùng. –

+0

Tại sao địa chỉ hoặc 'thanh' có thể không phải là duy nhất? –

Các vấn đề liên quan