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

问题描述:

假设我们有下面的代码:字符串文字模板 - 不同的行为编译器的

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

int main() 
{ 
    foo("str"); 
} 

Demonstration

GCC 4.7.2,铛3.2,ICC 13.0.1

未定义引用'void foo < char [4]>(char const(& )[4])”

MSVC-11.0

解析外部符号“空隙__cdecl FOO < 字符常量 [4]>(炭 常量(&)[4]) “(?? $ foo的@ $$ BY03 $$ CBD @@ YAXAAY03 $$ CBD @ Z)

通知char[4]在第二O的第一输出和char const[4]本安输出。

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

+7

两者都在抱怨没有'foo'定义.... – UmNyobe 2013-03-19 13:57:34

+0

@UmNyobe是的,我知道,它只需要打印类型 – FrozenHeart 2013-03-19 13:59:20

+0

@UmNyobe我的意思是什么类型应该在这种情况下 - char [4]或char const [4]? – FrozenHeart 2013-03-19 14:00:08

GCC是正确的;这const在VS的模板参数列表不应该存在:进行这种替代

[C++11: 14.8.2/3]:后,在8.3.5中描述的函数参数类型调整执行。 [示例:参数类型“void()(const int, int[5])”变成“void(*)(int,int*)”- 结束示例][注意:函数参数声明中的*限定符不影响函数 类型,但仍影响函数中函数参数变量的类型。 末端音符][实施例:

template <class T> void f(T t); 
template <class X> void g(const X x); 
template <class Z> void h(Z, Z*); 

int main() { 
    // #1: function type is f(int), t is non const 
    f<int>(1); 

    // #2: function type is f(int), t is const 
    f<const int>(1); 

    // #3: function type is g(int), x is const 
    g<int>(1); 

    // #4: function type is g(int), x is const 
    g<const int>(1); 

    // #5: function type is h(int, const int*) 
    h<const int>(1,0); 
} 

末端示例]

(实施例4是相关的。)

[C++11: 14.8.2/5]:将所得使用替代和调整的函数类型作为f的类型模板论证扣除的联合模板。[..]

而且可能相关:

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

  • 如果A是数组类型,则使用由数组到指针标准转换(4.2)生成的指针类型来代替A用于类型扣除;否则,
  • 如果A是函数类型,则使用由函数指针标准转换(4.3)生成的指针类型来代替类型推导的A;否则,
  • 如果AA“S型的CV-限定的类型,顶层CV-预选赛类型扣除被忽略
+1

我们都知道这一点,但它是如何适用于有关案件? – 2013-03-19 14:37:02

+0

@JamesKanze:'const'不是函数类型的一部分,函数类型用于模板参数推导。因此,在这种情况下'T'不应该是'const',但在OP的例子中MSVC已经这样做了。 – 2013-03-19 14:37:42

+0

我确实认为我可能会错过几个步骤。 :(想到我会把它扔到那里,尽管如此 – 2013-03-19 14:40:27

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 
} 

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

[... ...]如果P是一个参考类型,P引用的类型用于类型推导。 [...]

这意味着该类型的扣将尝试匹配T const针对int(在情况1)和针对int const(在情况2)。在第二种情况下,用int代替T将产生完美匹配,所以很容易;在第一种情况下,我们有const开始有一个完美的匹配。但是,这是§14.8.2.1/4的用武之地:

[...] 如果原来的P是引用类型,推导A(由参考简称,即,类型) 可以是 更CV-合格比转化A. [...]

在这里,替换为intT给我们带来了推导int const,这是更CV-合格比int(的类型参数x)。但由于上述第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); 
} 

除此之外,我换成intchar []的事实,这是例子,我第一个例子在结构上是相同的。为了说明为什么这等价成立,考虑下面的断言(不上触发任何编译器,如预期):

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

的C++标准11本任务中的行为3.9.3款/ 2 :

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

段落8.3.4/1也规定:

[...]任何类型的形式是“NT的CV-限定符-SEQ阵列”的被调整到“N CV的阵列 -qualifier-seq T“,对于”T的未知范围数组“也是类似的。可选的属性说明符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” 

末端示例] [注:所谓“N- CV-限定符-SEQ的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]

最后回到你原来的例子。由于字符串文字"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 
} 
+0

但为什么'std :: add_const :: type'与char const [4]'相同?这似乎并不明显 - 第一个是4个字符的常量数组,而第二个是4个常量字符的数组。 – interjay 2013-03-19 21:53:54

+0

@interjay:请参见C++ 11标准的3.9.3/2:“[...]应用于数组类型的任何cv限定符影响数组元素类型,而不是数组类型(8.3.4)。” – 2013-03-19 23:25:18

+0

@interjay:我在最后一次更新中对此进行了扩展 – 2013-03-19 23:29:56