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 の出番です。int
int const
int
T
const
[...]元の P が参照型である場合、推定された A (つまり、参照によって参照される型)は、変換された A よりもcv 修飾されている可能性があります。 [...]
ここでint
forT
を置き換えると、 (引数の型)int const
よりも cv 修飾された deduced が得られます。しかし、それは上記の 14.8.2.1/4 のために許容されるので、この場合でも であると推定されます。int
x
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);
}
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 const
char const[4]
A
A
char[4]
T
char const[4]
T
char[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
}