模板中的字符串文字 - 编译器的不同行为

Fro*_*art 12 c++ templates language-lawyer

假设我们有以下代码:

template <typename T>
void foo(const T&);

int main()
{
   foo("str");
}
Run Code Online (Sandbox Code Playgroud)

示范

gcc 4.7.2,clang 3.2,icc 13.0.1

未命名的引用`void foo < char [4]>(char const(&)[4])'

MSVC-11.0

未解析的外部符号"void __cdecl foo < char const [4]>(char const(&)[4])"(?? $ foo @ $$ BY03 $$ CBD @@ YAXAAY03 $$ CBD @ Z)

请注意char[4]第一个输出和char const[4]第二个输出.

为什么?谁是对的?你能引用这个标准吗?

And*_*owl 5

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
}
Run Code Online (Sandbox Code Playgroud)

这里发生了什么事?首先,根据§14.8.2.1/3:

[...]如果P是引用类型,则P引用的类型用于类型推导.[...]

这意味着这种类型的扣将尝试匹配T constint(在情况1)和对int const(在情况2).在第二种情况下,替换intT将产生一个完美的比赛,所以这是很容易; 在第一种情况下,我们const正在努力实现完美匹配.但这是§14.8.2.1/4发挥作用的地方:

[...] 如果原始P是参考类型,推导出的A(即参考文献所指的类型)可以比转换后的A. 更具有cv资格.[...]

在这里,替换intT我们提供了推导int const,它比int(参数的类型x)更符合cv .但由于上面的§14.8.2.1/4,这是可以接受的,因此即使在这种情况下T也被推断为int.

现在让我们解决您的原始示例(稍微调整一下,但我们最终会得到原始版本):

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);
}
Run Code Online (Sandbox Code Playgroud)

除此之外,我所替换intchar [],这是例子,第一个例子是在结构上是相同的.要了解为什么这个等价性成立,请考虑下面的断言(不会像预期的那样触发任何编译器):

// Does not fire
static_assert(
    std::is_same<
        std::add_const<char [4]>::type,
        char const[4]
    >::value, "Error");
Run Code Online (Sandbox Code Playgroud)

C++ 11标准规定了第3.9.3/2段中的这种行为:

应用于数组类型的任何cv限定符都会影响数组元素类型,而不是数组类型(8.3.4).

第8.3.4/1段还规定:

[...]任何类型的形式"NT的cv-qualifier-seq数组"被调整为"N cv-qualifier-seq T的数组",并且类似地用于"T的未知界限的数组".可选的attribute-specifier-seq属于数组.[例如:

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”
Run Code Online (Sandbox Code Playgroud)

-end example] [注意:"N cv-qualifier-seq T的数组"具有cv限定类型; 见3.9.3. - 尾注]

由于现在很清楚这两个示例表现出相同的模式,因此应用相同的逻辑是有意义的.这将引导我们走过同样的推理道路.

在执行类型扣除时,在第一种情况下T const匹配,char[4]char const[4]第二种情况下与之匹配.

在第二种情况下,T = char[4]产生完美匹配,因为在替换之后T const变为完全匹配char const[4].在第一种情况下,推断出的A是再次比原来更合格的A,取代char[4]T收益率char const[4].但话说回到14.8.2.1/4允许,所以T应该推断为char[4].

最后,回到原来的例子.由于字符串文字"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
}
Run Code Online (Sandbox Code Playgroud)


Lig*_*ica 1

海湾合作委员会是正确的;在 VS 的模板参数列表const中不应该存在:

\n\n
\n

[C++11: 14.8.2/3]:执行此替换后,将执行 8.3.5 中描述的功能参数类型调整。[ 示例:的参数类型\xe2\x80\x9cvoid ()(const int, int[5])\xe2\x80\x9d变为\xe2\x80\x9cvoid(*)(int,int*)\xe2\x80\x9d\xe2\x80\x94end 示例] [ 注意:函数参数声明中的顶级限定符不会影响函数\n 类型,但仍会影响函数内函数参数变量的类型。\xe2\x80\x94end 注] [ 示例:

\n\n
template <class T> void f(T t);\ntemplate <class X> void g(const X x);\ntemplate <class Z> void h(Z, Z*);\n\nint main() {\n  // #1: function type is f(int), t is non const\n  f<int>(1);\n\n  // #2: function type is f(int), t is const\n  f<const int>(1);\n\n  // #3: function type is g(int), x is const\n  g<int>(1);\n\n  // #4: function type is g(int), x is const\n  g<const int>(1);\n\n  // #5: function type is h(int, const int*)\n  h<const int>(1,0);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\x94结束示例]

\n
\n\n

(示例 4 是相关的。)

\n\n
\n

[C++11: 14.8.2/5]: 替换和调整后的函数类型用作模板参数推导的函数模板类型。 [..]

\n
\n\n

也可能相关:

\n\n
\n

从函数调用推导模板参数
\n [C++11: 14.8.2.1/2]:如果P不是引用类型:

\n\n
    \n
  • 如果A是数组类型,则使用数组到指针标准转换(4.2)生成的指针类型来代替A类型推导;否则,
  • \n
  • 如果A是函数类型,则使用函数到指针标准转换(4.3)生成的指针类型来代替A类型推导;否则,
  • \n
  • 如果是 cv 限定类型,则类型推导将忽略 \xe2\x80\x99s 类型的A顶级cv 限定符A
  • \n
\n
\n

  • @LightnessRacesinOrbit 你在哪里看到的。OP 代码中没有顶级 const;在这种情况下,“int”和“int const”被认为是相同的类型,但“int&amp;”和“int const&amp;”则不是。 (2认同)