12

次のコードがあるとします。

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

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

デモンストレーション

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]最初の出力と2 番目の出力に注目してくださいchar const[4]

なんで?そして、誰が正しいですか?基準を引用できますか?

4

2 に答える 2

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
}

ここで何が起こっているのですか?まず、§ 14.8.2.1/3 に従って:

[...] P が参照型の場合、P によって参照される型が型推定に使用されます。[...]

これは、型推論が(ケース 1 の場合) および(ケース 2 の場合)T constに対して一致しようとすることを意味します。2 番目のケースでは、 forを代入すると完全に一致するので、簡単です。最初のケースでは、完全に一致するように進んでいます。しかし、これが § 14.8.2.1/4 の出番です。intint constintTconst

[...]元の P が参照型である場合、推定された A (つまり、参照によって参照される型)は、変換された A よりもcv 修飾されている可能性があります。 [...]

ここでintforTを置き換えると、 (引数の型)int constよりも cv 修飾された deduced が得られます。しかし、それは上記の 14.8.2.1/4 のために許容されるので、この場合でも であると推定されます。intxTint

元の例に取り組みましょう (わずかに調整されていますが、最終的に元のバージョンに到達します)。

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

intに置き換えたことを除けばchar []、これは例であり、最初の例は構造が同じです。この同等性が成立する理由を確認するには、以下のアサーションを検討してください (予想どおり、どのコンパイラでも起動しません)。

// 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-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”

— 例の終了 ] [ 注: 「N 個の cv-qualifier-seq T の配列」には cv-qualified 型があります。3.9.3 を参照してください。—終わりのメモ]

2 つの例が同じパターンを示していることが明らかになったので、同じロジックを適用することは理にかなっています。そして、それは私たちをまったく同じ推論の道に導きます.

型推論を実行している間、最初のケースでT constは一致し、2 番目のケースでは一致します。char[4]char const[4]

2 番目のケースでは、置換後に になるT = char[4]ため、完全に一致します。最初のケースでは、推定されたものは、yieldを置き換えるという点で、オリジナルよりも cv 修飾されています。しかし、これも 14.8.2.1/4 で許可されているため、.T constchar const[4]AAchar[4]Tchar const[4]Tchar[4]

最後に、元の例に戻ります。文字列リテラル"str"も type を持っているchar const[4]ので、Tであると推定されるべきであり、これはGCC が正しいchar [4]ことを意味します:

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
}
于 2013-03-19T16:15:37.907 に答える
1
于 2013-03-19T14:35:37.773 に答える