2015-06-18 19 views
6

Điều này có vẻ là một đối tượng khác "đang làm tốt điều đó?" câu hỏi từ gcc 6.0.0 và clang 3.7.0 hoạt động khác nhau.Chuyển tiếp đối số không loại gây ra hành vi khác nhau trên Mẫu biến số

Giả sử chúng ta có một mẫu biến mà phải mất một const char * như mẫu đối số không và chuyên cho một con trỏ đưa ra:

constexpr char INSTANCE_NAME[]{"FOO"}; 

struct Struct{ void function() const { std::cout << __PRETTY_FUNCTION__; } }; 
std::ostream &operator <<(std::ostream &o, const Struct &) { return o << INSTANCE_NAME; } 

template <const char *> char Value[]{"UNKNOWN"}; 
// spezialization when the pointer is INSTANCE_NAME 
template <   > Struct Value<INSTANCE_NAME>{}; 

Lưu ý rằng biến mẫu có các loại khác nhau tùy thuộc vào chuyên môn hóa. Mười chúng tôi có hai chức năng template, mỗi một mất một const char * như mẫu đối số không và chuyển tiếp nó vào mẫu biến:

template <const char *NAME> void print() 
{ 
    std::cout << Value<NAME> << '\n'; 
} 

template <const char *NAME> void call_function() 
{ 
    Value<NAME>.function(); 
} 

Sau đó, kêu gọi này chức năng kết quả trong các hành vi khác nhau:

int main() 
{ 
    print<INSTANCE_NAME>(); 
    call_function<INSTANCE_NAME>(); 

    return 0; 
} 

Code Here

kêu vang 3.7.0 in FOOvoid Struct::function() const (Như tôi đã mong đợi) trong khi 6.0.0 gcc thất bại trong việc biên dịch với các lỗi dưới đây:

yêu cầu cho thành viên 'chức năng' trong 'Giá trị', mà là loại phi lớp 'char [8]'

tôi gần như chắc chắn rằng gcc thất bại trong việc mong đối số mẫu không loại NAME để biến mẫu Value trong hàm call_function và vì lý do này nó chọn mẫu biến không chuyên là một với 'char [8]' loại ...

Nó hoạt động như đang sao chép đối số mẫu. Điều này chỉ xảy ra khi gọi chức năng thành viên của đối tượng, nếu chúng tôi nhận xét nội dung của call_function, đầu ra là FOO không UNKNOWN, do đó, trong chức năng print chuyển tiếp đang hoạt động ngay cả trong gcc.

Vì vậy

  • hành vi đúng là gì? (mi bet là dành cho clang)
  • Làm thế nào tôi có thể mở một vé lỗi cho trình biên dịch đang làm sai?
+0

@ BЈовић bạn có thể, miễn là 'const char *' có liên kết bên ngoài ([xem câu trả lời này] (http://stackoverflow.com/a/16402606/499359)). Với liên kết bên ngoài, nó sẽ luôn có cùng một địa chỉ; nghĩ về nó như thể nó là một 'int'. –

+2

Chỉ cần lưu ý: có các cấu trúc hoàn toàn khác được gọi là 'FOO',' Foo' và 'foo' làm cho việc phân tích cú pháp tinh thần ví dụ của bạn trở nên khó khăn. 'MyCharP',' MyStruct' và 'myFun' hoặc tương tự sẽ dễ dàng hơn. – TartanLlama

+0

@TartanLlama Tôi đã thay đổi tên, nhờ gợi ý :) –

Trả lời

3

Có một sự đồng thuận hợp lý rằng mẫu chuyên ngành biến được phép thay đổi kiểu mẫu biến: C++1y/C++14: Variable Template Specialization?

Hành vi của gcc là đặc biệt thú vị nếu kiểu mặc định của Value được thay đổi thành một loại với một phương pháp function:

struct Unknown{ void function() const { std::cout << __PRETTY_FUNCTION__; } }; 
template <const char *> Unknown Value; 

prog.cc: In instantiation of 'void call_function() [with const char* NAME = ((const char*)(& INSTANCE_NAME))]': 
prog.cc:26:18: required from here 
prog.cc:20:5: error: 'Unknown::function() const' is not a member of 'Struct' 
    Value<NAME>.function(); 
    ^

các lỗi dường như là nơi không chuyên template biến có một kiểu mà không phụ thuộc vào các thông số mẫu template biến, gcc giả trong các phương thức mẫu sử dụng mẫu biến đó mà mẫu biến luôn có loại đó.

Cách giải quyết khác, như thường lệ, là chuyển tiếp mẫu biến đổi vô điều kiện thành mẫu lớp với chuyên môn của mẫu lớp và với sự cần thiết cần thiết để tuân thủ ODR.

Giải pháp khác (có thể dễ dàng hơn) là làm cho loại mẫu biến không chuyên biệt bằng cách nào đó phụ thuộc vào các tham số mẫu của biến mẫu; trong trường hợp của bạn này sẽ làm việc:

template <const char *P> decltype(*P) Value[]{"UNKNOWN"}; 

tôi không thể tìm thấy một vấn đề tương ứng trong gcc bugzilla vì vậy bạn có thể muốn nhập một cái mới. Dưới đây là một ví dụ tối thiểu:

struct U { void f() {} }; 
struct V { void f() {} }; 
template<class T> U t; 
template<> V t<int>; 
template<class T> void g() { t<T>.f(); } 
int main() { g<int>(); } 
+0

Tôi đã thêm vấn đề (hy vọng tôi đã làm tốt, là báo cáo lỗi đầu tiên của tôi) Tôi đã sử dụng ví dụ và văn bản của bạn (vì tiếng Anh không phải là ngôn ngữ đầu tiên của tôi và tôi không chắc chắn liệu tôi có đang viết hay không) một cái gì đó kỳ lạ), cảm ơn rất nhiều. –

+0

@PaperBirdMaster có vẻ tốt; Tôi đã thêm đầu ra lỗi. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66596 – ecatmur

5

Điều thú vị là GCC thậm chí còn tự mâu thuẫn trong ví dụ này.

Cho phép khai báo một lớp mẫu không đầy đủ mà nên cung cấp một số thông điệp biên dịch tốt đẹp mà chúng ta có thể lạm dụng:

template <typename T> 
struct type_check; 

Chúng tôi cũng sẽ thực hiện một const char* rằng chúng ta có thể sử dụng để thử nghiệm:

constexpr char NOT_FOO[]{"NOT_FOO"}; 

Bây giờ chúng ta sẽ thấy những gì trình biên dịch cuộn cảm trên:

template <const char *NAME> void foo() 
{ 
    type_check<decltype(Value<FOO>)> a; 
    type_check<decltype(Value<NAME>)> b; 
    type_check<decltype(Value<NOT_FOO>)> c; 
    type_check<decltype(Value<FOO>.foo())> d; 
    type_check<decltype(Value<NAME>.foo())> e; 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 
} 

H ere là những lỗi mà GCC 5.1.0 tạo ra (chỉnh sửa một chút để rõ ràng):

test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type 
    type_check<decltype(Value<FOO>)> a; 
            ^
test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type 
    type_check<decltype(Value<NAME>)> b; 

test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type 
    type_check<decltype(Value<NOT_FOO>)> c; 
            ^
test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type 
    type_check<decltype(Value<FOO>.foo())> c; 

test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NAME>.foo())> d; 

test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 

Chúng ta hãy thực hiện từng bước một.


Lỗi 1:

test.cpp:21:38: error: ‘type_check<Foo> a’ has incomplete type 
    type_check<decltype(Value<FOO>)> a; 

Trong lỗi đầu tiên, chúng ta có thể thấy rằng GCC đúng deduces rằng loại Value<FOO>Foo. Đây là những gì chúng tôi mong đợi.

Lỗi 2:

test.cpp:22:39: error: ‘type_check<Foo> b’ has incomplete type 
    type_check<decltype(Value<NAME>)> b; 

Ở đây, GCC chính xác hiện chuyển tiếp và làm việc ra rằng Value<NAME> là loại Foo.

Lỗi 3:

test.cpp:25:42: error: ‘type_check<char [8]> c’ has incomplete type 
    type_check<decltype(Value<NOT_FOO>)> c; 

Tuyệt vời, Value<NOT_FOO>"UNKNOWN", vì vậy đây là đúng.

Lỗi 4:

test.cpp:23:44: error: ‘type_check<void> c’ has incomplete type 
    type_check<decltype(Value<FOO>.foo())> c; 

này là tốt, Value<FOO>Foo, mà chúng tôi có thể gọi foo trên, trở về void.

Lỗi 5:

test.cpp:24:37: error: request for member ‘foo’ in ‘Value<NAME>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NAME>.foo())> d; 

Đây là một số lẻ. Mặc dù trong lỗi 2 chúng ta có thể thấy rằng GCC biết rằng loại Value<NAME>Foo, khi nó cố gắng tìm kiếm chức năng foo, nó bị sai và sử dụng mẫu chính thay thế. Điều này có thể là một số lỗi trong tra cứu hàm không giải quyết chính xác các giá trị của các đối số mẫu không phải kiểu.

Lỗi 6:

test.cpp:28:40: error: request for member ‘foo’ in ‘Value<((const char*)(& NOT_FOO))>’, which is of non-class type ‘char [8]’ 
    type_check<decltype(Value<NOT_FOO>.foo())> f; 

Ở đây chúng ta có thể thấy các trình biên dịch một cách chính xác chọn mẫu chính khi làm việc ra Value<NOT_FOO> là gì. Điều mà tôi quan tâm là (const char*)(& NOT_FOO)) mà GCC suy ra là loại NOT_FOO. Có lẽ đây là một con trỏ đến vấn đề? Tôi không chắc.


Tôi sẽ đề xuất gửi lỗi và chỉ ra sự khác biệt. Có lẽ điều này không hoàn toàn trả lời câu hỏi của bạn, nhưng tôi hy vọng nó sẽ giúp.

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