6

以下のような条件で問題が発生しました。

#include <iostream>
#include <type_traits>

#define TRACE void operator()() const { std::cerr << "@" << __LINE__ << std::endl; }

template <class T>
struct check : std::true_type {};

template <class F, class T, class Check=void>
struct convert {
  TRACE;// first case
};

template <class F, class T>
struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> {
  TRACE; // second case
};

template <class T>
struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> {
  TRACE; // third case
};

それで

convert<int*, int> c;
c();

g++-4.5、g++-4.6、g++-4.7、clang++-3.1 (すべてオプション -std=c++0x を使用) であいまいなクラス テンプレートのインスタンス化が報告されます。

しかし、3番目のケースのチェックを次のように置き換えると

typename std::enable_if<(check<int>::value && check<T>::value), void>:type

その後、clang++-3.1 は正常に動作します。

コンパイラのバグですか、それとも標準ですか?

4

2 に答える 2

1

同様の問題がこの質問で発生しました

2 番目と 3 番目の部分的な特殊化の両方が に一致するためconvert<int*, int>、コンパイラは、引数として指定された 2 つの部分的に特殊化されたクラス テンプレートを使用して 2 つのテスト関数テンプレートを構築します。

template <class F, class T> 
void fun2(convert<F*, T, typename std::enable_if<
    (check<F>::value && check<T>::value), void>::type>
);

template <class T> 
void fun3(convert<int*, T, typename std::enable_if<
    (check<T>::value), void>::type>
);

次に、コンパイラは、1 つの関数の変換されたパラメーターのセットを別の関数に相互置換することによって、1 つの関数テンプレートが他のテンプレートよりも特殊化されているかどうかを確認し、すべてのテンプレート引数を推定できるかどうかを確認します。これが両方の方法で機能する場合、どちらの機能も他方よりも専門化されておらず、あいまいさが生じます。

ここでの問題std::enable_if< (check<F>::value && check<T>::value), void>::type>は、 が非推定コンテキストであり、この引数推定ゲーム中に評価されないことです。コンパイラーは、一般式が同じ構造形式 (::区切り文字の前にあるものはすべて推定される) を持っているかどうかのみをチェックし、同じ値 (true_typeこの場合) を持っているかどうかはチェックしません。

3 番目の部分的な特殊化にエクストラを追加することによってのみcheck<int>::value、3 番目の特殊化は 2 番目よりも特殊化されます。もう 1 つの "修正" はtrue_typeCheckパラメーターに手動で入力することですが、コンパイラーは引数の推定中にそれを行いません。

UPDATE : Johannes Schaub への対応 - litb: その通りです。 に挿入されたコードは、std::check<int>Ideonestd::enable_ifおよび MSVC++ 2010 ではコンパイルされません。どうすればよいですか? 14.8.2.4 節 11によると[temp.deduct.partial]

ほとんどの場合、演繹が成功するためには、すべてのテンプレート パラメーターに値が必要ですが、部分的な順序付けの目的で、テンプレート パラメーターは、部分的な順序付けに使用される型で使用されていない限り、値を持たないままにすることができます。[ 注: 非推定コンテキストで使用されるテンプレート パラメータは、使用されていると見なされます。— エンドノート ] [例:

template <class T> T f(int); // #1
template <class T, class U> T f(U); // #2
void g() {
    f<int>(1); // calls #1
}

OPによるコードの場合、未使用のパラメーターがstd::enable_if式になると解釈します。私の推測では、Clang 3.1 では、Ideone と MSVC++ では行われない式の一致が行われると思います。上記の引用のコンテキストで、これが標準で必要かどうかわかりません: 未使用のテンプレート パラメーターのみを無視するか、未使用のテンプレート式も無視する必要がありますか? 標準の他の部分では、「実装に英雄的な努力を要求しない」などのフレーズが表示されます。おそらく、Clang はこの点で MSVC++ や Ideone よりも勇敢です。

于 2012-07-16T09:04:18.920 に答える
0

あなたが持っている

template <class F, class T>
struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> {
  TRACE; // second case
};

template <class T>
struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> {
  TRACE; // third case
};

を使用するconvert<int*, int> c;と、コンパイラは使用する必要がある構造体を選択できません。どちらも適合するためです。

check<F>::value最初のテンプレートで使用することに注意してください。つまり、たとえば an を渡しint *てもcheck<int>::value、ないcheck<int *>::value

于 2012-07-16T07:44:40.263 に答える