C++ 转发非类型参数会导致变量模板上的不同行为

C++ 转发非类型参数会导致变量模板上的不同行为,c++,c++14,template-specialization,variable-templates,C++,C++14,Template Specialization,Variable Templates,这似乎是另一个“谁做得好?”的问题,因为gcc 6.0.0和Clang3.7.0的行为不同 假设我们有一个变量模板,它将const char*作为非模板参数,并专门用于给定指针: constexpr char INSTANCE_NAME[]{"FOO"}; struct Struct{ void function() const { std::cout << __PRETTY_FUNCTION__; } }; std::ostream &operator <<(

这似乎是另一个“谁做得好?”的问题,因为gcc 6.0.0和Clang3.7.0的行为不同

假设我们有一个变量模板,它将
const char*
作为非模板参数,并专门用于给定指针:

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>{};
然后,调用此函数会导致不同的行为:

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

    return 0;
}
intmain()
{
打印();
调用函数();
返回0;
}

clang 3.7.0打印
FOO
void Struct::function()const
(如我所料),而gcc 6.0.0未能编译,错误如下:

请求“Value”中的成员“function”,该成员属于非类类型“char[8]”

我几乎可以肯定,gcc未能将模板非类型参数
NAME
转发到函数
call\u函数
中的变量模板
Value
,因此它选择了带有
'char[8]'
类型的非专用变量模板

它的行为就像复制模板参数一样。这仅在调用对象的成员函数时发生,如果我们对
call\u函数的主体进行注释,则输出是
FOO
而不是
UNKNOWN
,因此在
print
函数中,即使在gcc中,转发也起作用

所以

  • 正确的行为是什么?(米赌是指叮当声)
  • 我如何才能为出错的编译器打开错误通知单

有一个合理的共识,即允许变量模板专门化来改变变量模板的类型:

如果将
Value
的默认类型更改为具有
函数
方法的类型,则gcc的行为特别有趣:

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();
     ^

struct Unknown{void function()const{std::cout有趣的是,在这个例子中,GCC甚至自相矛盾

让我们声明一个不完整的模板类,它应该给出一些我们可以滥用的好的编译器消息:

template <typename T>
struct type_check;
现在我们将看到编译器阻塞了什么:

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;
}
在这里,GCC正确地进行转发,并计算出
Value
属于
Foo
类型

错误3:

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;
这很好,
Value
Foo
,我们可以调用
Foo
on,返回
void

错误5:

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;
在这里,我们可以看到编译器在计算
值时正确地选择了主模板。我感兴趣的是
(const char*)(&NOT_FOO))
,GCC将其推断为
NOT_FOO
的类型。也许这是指向问题的指针?我不确定



我建议您填写一个bug并指出其中的差异。也许这并不能完全回答您的问题,但我希望它能有所帮助。

@Bћ您可以,只要
常量字符*
具有外部链接()使用外部链接时,它总是有相同的地址;把它想象成一个
int
。只需注意:有完全不同的结构,称为
FOO
FOO
FOO
,这会让你很难在心里分析你的例子。
MyCharP
MyStruct
myFun
或similar会更简单。@TartanLlama我已经更改了名称,谢谢你的建议:)即使不调用
call_function()
main
中,gcc似乎试图实例化
调用函数
。你需要模板参数为
const char*
才能工作吗?如果
int
工作,专门化为
0
?我已经添加了这个问题(希望我做得好,这是我的第一个bug报告)我使用了你的例子和文本(因为英语不是我的第一语言,我不确定我是否写了一些奇怪的东西),非常感谢。@PaperBirdMaster看起来不错;我添加了错误输出。