22

GCC 4.7.2 と Clang 3.1 の両方でいくつかの C++11 コードをコンパイルしているときに、Clang が GCC が成功するテンプレート引数を推測できないという問題に遭遇しました。より抽象的な形式では、コードは次のようになります。

ソース/test.cc:

struct Element {
};

template <typename T>
struct FirstContainer {
};

template <typename T, typename U = Element>
struct SecondContainer {
};

template <template <typename> class Container>
void processOrdinary(Container<Element> /*elements*/) {
}

template <template <typename, typename> class Container>
void processOrdinary(Container<Element, Element> /*elements*/) {
}

template <template <typename, typename...> class Container>
void processVariadic(Container<Element> /*elements*/) {
}

int main() {
  // This function instantiation works in both GCC and Clang.
  processOrdinary(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processOrdinary(SecondContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic<SecondContainer>(SecondContainer<Element>{});
  // This function instantiation works in GCC but not in Clang.
  processVariadic(SecondContainer<Element>{});
  return 0;
}

標準の §14.3.3 の例と §14.8.2 の仕様を読むことから、推論は機能するはずですが、確かなことは言えません。これは、ビルドから得られる出力です。

mkdir -p build-gcc/
g++ -std=c++0x -W -Wall -Wextra -Weffc++ -pedantic -c -o build-gcc/test.o src/test.cc
g++  -o build-gcc/test build-gcc/test.o
mkdir -p build-clang/
clang++ -std=c++11 -Weverything -Wno-c++98-compat -c -o build-clang/test.o src/test.cc
src/test.cc:34:3: error: no matching function for call to 'processVariadic'
  processVariadic(SecondContainer<Element>{});
  ^~~~~~~~~~~~~~~
src/test.cc:21:6: note: candidate template ignored: failed template argument deduction
void processVariadic(Container<Element> /*elements*/) {
     ^
1 error generated.
make: *** [build-clang/test.o] Fel 1

結果が異なるのはなぜですか?GCC はずさんで、Clang はばかげていますか、私のコードには未指定の動作が含まれていますか、それともそれらすべてですか?

4

1 に答える 1

7

Clang は、この呼び出しの引数を推測しようとしています:

processVariadic(SecondContainer<Element>{});

にはデフォルトのテンプレート引数があるためSecondContainer、これは次と同等です。

processVariadic(SecondContainer<Element, Element>{});

Pしたがって、 =Container<Element>およびA=を使用してテンプレート引数推定を実行しますSecondContainer<Element, Element>Containerテンプレート パラメータがであるとすぐに推測できますSecondContainer

次に、テンプレート引数を検討します。引数の型は完全に解決されているため、Clang は、パラメーターが同じ数の型を持っている必要があると考えています。したがって、控除の失敗を示します。


それで、何が起こるはずですか?の言葉で[temp.deduct.type]p8

テンプレート型引数T、テンプレート テンプレート引数TT、またはテンプレート非型引数 iは、次の形式のいずれかである場合に推定できます: P[ ...]は、少なくとも 1 つの引数に が含まれるテンプレート引数リストを表し、テンプレート引数を表します。少なくとも 1 つの引数に が含まれるリストおよび を表すテンプレート引数リスト 引数に aまたはが含まれないリスト。A
[...]
TT<T>
TT<i>
TT<>
<T>T<i>i<>Ti

テンプレートの引数を一致させるために、次のようにし[temp.deduct.type]p9ます。

がまたはPを含む形式を持つ場合、それぞれのテンプレート引数リストの各引数が、 の対応するテンプレート引数リストの対応する引数と比較されます。<T><i>PiPAiA

ここで注意すべき点が 2 つあります。Pi1 つは、この規則ではリストとが異なる長さの場合に何が起こるかを示していないことAi(この場合のように) であり、一般的な解釈では、一致しない項目は調べられないようです。もう 1 つは、 の形式にはorPが含まれていないため (テンプレート パラメーターが含まれていないため、単に が含まれているため) 、この規則には従うべきではないということです。<T><i><>


したがって、Clang がこのコードを拒否したのは誤りです。r169475で修正しました。

于 2012-12-06T06:57:37.680 に答える