C++ 模板中的字符串文字-编译器的不同行为
假设我们有以下代码:C++ 模板中的字符串文字-编译器的不同行为,c++,templates,language-lawyer,C++,Templates,Language Lawyer,假设我们有以下代码: template <typename T> void foo(const T&); int main() { foo("str"); } 模板 void foo(const T&); int main() { 富(str); } gcc 4.7.2、clang 3.2、icc 13.0.1 对“void foo(char const(&)[4])的未定义引用 MSVC-11.0 未解析的外部符号“void\uu cdecl foo(char
template <typename T>
void foo(const T&);
int main()
{
foo("str");
}
模板
void foo(const T&);
int main()
{
富(str);
}
gcc 4.7.2、clang 3.2、icc 13.0.1
对“void foo(char const(&)[4])的未定义引用
MSVC-11.0
未解析的外部符号“void\uu cdecl foo(char
常量(&)[4])”(??$foo@$$BY03$$CBD@@YAXAAY03$$CBD@Z)
注意第一个输出中的char[4]
,第二个输出中的char const[4]
为什么??谁是对的?你能引用标准吗?GCC是正确的;VS模板参数列表中的
const
不应存在:
[C++11:14.8.2/3]:
执行此替换后,将执行8.3.5中描述的功能参数类型调整。[示例:参数类型“void()(const int,int[5])”
变为“void(*)(int,int*)”
-结束示例][注:函数参数声明中的顶级限定符不会影响函数
类型,但仍影响函数中函数参数变量的类型。-结束说明][示例:
模板无效f(T);
模板空隙g(常数X X);
模板空隙h(Z,Z*);
int main(){
//#1:函数类型为f(int),t为非常量
f(1);
//#2:函数类型为f(int),t为常数
f(1);
//#3:函数类型为g(int),x为常数
g(1);
//#4:函数类型为g(int),x为常数
g(1);
//#5:函数类型为h(int,const int*)
h(1,0);
}
-[结束示例]
(示例4是相关示例。)
[C++11:14.8.2/5]:
生成的替换和调整函数类型用作模板参数推断的函数模板类型。[…]
也可能相关:
从函数调用推断模板参数[C++11:14.8.2.1/2]:
如果P
不是参考类型:
- 如果
是数组类型,则使用数组到指针标准转换(4.2)产生的指针类型代替A
进行类型推断;否则,A
- 如果
是函数类型,则使用函数到指针标准转换(4.3)产生的指针类型代替A
进行类型推断;否则,A
- 如果
是cv限定类型,则在类型扣除时忽略A
类型的顶级cv限定符A
GCC是对的。 让我们从一个稍微简单的示例开始,然后证明原始示例遵循相同的模式:
template<typename T>
void bar(T const&)
{
// Shall not fire
static_assert(std::is_same<T, int>::value, "Error!");
}
int main()
{
int x = 0;
bar(x); // 1 - Assertion won't fire
int const y = 0;
bar(y); // 2 - Assertion won't fire
}
除了将int
替换为char[]
之外,这是一个示例,我的第一个示例在结构上是相同的。若要理解这种等价性的原因,请考虑下面的断言(如预期的那样,它对任何编译器都不起作用):
-结束示例][注:“N cv限定符序列T的数组”具有cv限定类型;请参见3.9.3.-结束注释]
因为现在很清楚,这两个例子展示了相同的模式,所以应用相同的逻辑是有意义的。这将引导我们走上同样的推理道路
在执行类型推断时,T const
在第一种情况下与char[4]
匹配,在第二种情况下与char const[4]
匹配
在第二种情况下,T=char[4]
产生一个完美的匹配,因为T const
在替换后变成char const[4]
。在第一种情况下,推导出的A
再次比原始A
更符合cv条件,因为用char[4]
替换T
会产生char const[4]
。但是,这也是14.8.2.1/4所允许的,因此T
应该被推断为char[4]
最后,回到您最初的示例。由于字符串literal“str”
也有类型char const[4]
,T
应该被推断为char[4]
,这意味着GCC是正确的:
template<typename T>
void foo(T const&)
{
// Shall not fire
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
foo("str"); // Shall not trigger the assertion
}
模板
无效foo(T常量&)
{
//不得开火
静态断言(std::is_same::value,“Error!”);
}
int main()
{
foo(“str”);//不应触发断言
}
两人都在抱怨没有定义foo
是的,我知道,需要打印类型only@UmNyobe我的意思是在这种情况下应该是什么类型-char[4]还是char const[4]?@NikitaTrophimov:这是个好问题,没有数组它非常简单,但我不确定const应该出现在哪里(如果有的话)。我的猜测是MSVC只是在做额外的工作。我们都知道这一点,但它如何应用于所讨论的情况?@JamesKanze:const
不是函数类型的一部分,函数类型用于模板参数推断。因此,在这种情况下,T
不应是const
,但在OP的示例中,MSVC已经是这样做的。我确实认为我可能遗漏了几个步骤(不过,我想我还是把它扔出去。const
是OP代码中函数类型的一部分。OP代码与您给出的示例之间的区别在于,OP代码中函数引用了const
,因此不能删除const
。@LightnessRacesinOrbit您在哪里看到的。Ther.)在OP的代码中,e不是顶级常量;int
和int-const
在此上下文中被认为是相同的类型,但是int&
和int-const&
不是。但是为什么std::add_-const::type
与char-const[4]相同呢
?这似乎并不明显-第一个是4个字符的常量数组,而第二个是4个常量字符的数组。@interjay:请参阅C++11标准的3.9.3/2:“[…]任何cv-q
template<typename T>
void bar(T const&)
{
// Does not fire in GCC, fires in VC11. Who's right?
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
char x[] = "foo";
bar(x);
char const y[] = "foo";
bar(y);
}
// Does not fire
static_assert(
std::is_same<
std::add_const<char [4]>::type,
char const[4]
>::value, "Error");
typedef int A[5], AA[2][3];
typedef const A CA; // type is “array of 5 const int”
typedef const AA CAA; // type is “array of 2 array of 3 const int”
template<typename T>
void foo(T const&)
{
// Shall not fire
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
foo("str"); // Shall not trigger the assertion
}