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

标签 c++ templates language-lawyer

假设我们有以下代码:

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

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

Demonstration

gcc 4.7.2、clang 3.2、icc 13.0.1

undefined reference to `void foo<char [4]>(char const (&) [4])'

MSVC-11.0

unresolved external symbol "void __cdecl foo<char const [4]>(char const (&)[4])" (??$foo@$$BY03$$CBD@@YAXAAY03$$CBD@Z)

注意第一个输出中的 char[4] 和第二个输出中的 char const[4]

为什么?谁是对的?请您引用标准吗?

最佳答案

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:

[...] If P is a reference type, the type referred to by P is used for type deduction. [...]

这意味着类型推导将尝试匹配 T const反对int (在情况 1)和反对 int const (在案例 2 中)。在第二种情况下,替换 int对于 T将产生完美的匹配,所以这很容易;在第一种情况下,我们有 const开始我们的完美匹配。但这就是第 14.8.2.1/4 条发挥作用的地方:

[...] If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A. [...]

这里,替换 int对于 T给我们一个推断的int const , 比 int 更符合 cv 要求(参数的类型 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 [] ,这是示例,我的第一个示例在结构上相同。要了解为什么这种等价性成立,请考虑下面的断言(正如预期的那样,它不会在 any 编译器上触发):

// 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 段中规定了这种行为:

Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4).

第 8.3.4/1 段还规定:

[...] Any type of the form “cv-qualifier-seq array of N T” is adjusted to “array of N cv-qualifier-seq T”, and similarly for “array of unknown bound of T”. The optional attribute-specifier-seq appertains to the array. [ Example:

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”

—end example ] [ Note: An “array of N cv-qualifier-seq T” has cv-qualified type; see 3.9.3. —end note ]

由于现在很清楚这两个示例展示了相同的模式,因此应用相同的逻辑是有意义的。这将引导我们走上同样的推理路径。

在进行类型推断时,T constchar[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
}

关于c++ - 模板中的字符串文字 - 编译器的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15501470/

相关文章:

c++ - 使用 SIMD 指令平滑样条

c++ - 将对象传递给函数时,如何防止模板化构造函数将类作为参数

c++ - 实现二进制搜索树时对类的怀疑

c++ - std::less<Pointer> 的严格总顺序

Java 注释 - 标识符或类型名称

c++ - 使用 C++ 的 MVC 方法

c++ - boost:asio::read 或 boost:asio::async_read 超时

c++ - 段错误(核心转储)linux

c++ - 重载的 operator<< 模板不适用于 std::list,尽管它适用于 std::vector

c++ - 如何为 friend 函数中定义的类授予友元?