简体   繁体   中英

String literal in templates - different behavior of compilers

Suppose we have the following code:

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)

Notice char[4] in the first output and char const[4] in the second output.

Why? And who's right? Can you quote the standard, please?

GCC is right.

Let's start from a slightly simpler example, and later prove that the original example follows the same pattern:

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
}

What's happening here? First of all, per § 14.8.2.1/3:

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

This means that type deduction will try to match T const against int (in case 1) and against int const (in case 2). In the second case, substituting int for T will yield a perfect match, so that's easy; in the first case, we have the const getting on our way to have a perfect match. But this is where § 14.8.2.1/4 comes into play:

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

Here, replacing int for T gives us a deduced int const , which is more cv-qualified than int (the type of the argument x ). But that is acceptable because of § 14.8.2.1/4 above, so even in this case T is deduced to be int .

Let's now tackle your original example (just slightly adjusted, but we'll get to the original version eventually):

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);
}

Apart from the fact that I replaced int with char [] , this is example and my first example are identical in structure. To see why this equivalence holds, consider the assertion below (which doesn't fire on any compiler, as expected):

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

The C++11 Standard mandates this behavior in Paragraph 3.9.3/2:

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

Paragraph 8.3.4/1 also specifies:

[...] Any type of the form “cv-qualifier-seq array of NT” 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 ]

Since it is now clear that the two examples exhibit the same pattern, it makes sense to apply the same logic. And that will lead us through the very same reasoning path.

While performing type deduction, T const is matched against char[4] in the first case and against char const[4] in the second case.

In the second case, T = char[4] yields a perfect match, because T const becomes char const[4] after the substitution. In the first case, the deduced A is once again more cv-qualified than the original A , in that substituting char[4] for T yields char const[4] . But then again, that is allowed by 14.8.2.1/4, so T should be deduced as char[4] .

Finally, back to your original example. Since the string literal "str" also has type char const[4] , T should be deduced to be char [4] , which means that GCC is right :

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
}

GCC is correct; that const in VS's template argument list should not be there:

[C++11: 14.8.2/3]: After this substitution is performed, the function parameter type adjustments described in 8.3.5 are performed. [ Example: A parameter type of “void ()(const int, int[5])” becomes “void(*)(int,int*)” . —end example ] [ Note: A top-level qualifier in a function parameter declaration does not affect the function type but still affects the type of the function parameter variable within the function. —end note ] [ Example:

 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); } 

—end example ]

(Example 4 is the pertinent one.)

[C++11: 14.8.2/5]: The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. [..]

Also possibly relevant:

Deducing template arguments from a function call
[C++11: 14.8.2.1/2]: If P is not a reference type:

  • If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is used in place of A for type deduction; otherwise,
  • If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3) is used in place of A for type deduction; otherwise,
  • If A is a cv-qualified type, the top level cv-qualifiers of A 's type are ignored for type deduction

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM