estranheza de modelo ambíguo
-
19-09-2019 - |
Pergunta
Eu tenho o seguinte código (desculpe pelo grande pedaço de código, mas não pude mais reduzi -lo)
template <bool B>
struct enable_if_c {
typedef void type;
};
template <>
struct enable_if_c<false> {};
template <class Cond>
struct enable_if : public enable_if_c<Cond::value> {};
template <typename X>
struct Base { enum { value = 1 }; };
template <typename X, typename Y=Base<X>, typename Z=void>
struct Foo;
template <typename X>
struct Foo<X, Base<X>, void> { enum { value = 0 }; };
template <typename X, typename Y>
struct Foo<X, Y, typename enable_if<Y>::type > { enum { value = 1 }; };
int main(int, char**) {
Foo<int> foo;
}
Mas não consegue compilar com o GCC (v4.3) com
foo.cc: In function ‘int main(int, char**)’:
foo.cc:33: error: ambiguous class template instantiation for ‘struct Foo<int, Base<int>, void>’
foo.cc:24: error: candidates are: struct Foo<X, Base<X>, void>
foo.cc:27: error: struct Foo<X, Y, typename enable_if<Y>::type>
foo.cc:33: error: aggregate ‘Foo<int, Base<int>, void> foo’ has incomplete type and cannot be defined
OK, então é ambíguo. Mas eu não esperava que fosse um problema, pois, ao usar a especialização, quase sempre será alguma ambiguidade. No entanto, este erro é acionado apenas ao usar a classe com enable_if<...>
, se eu substituí -lo por uma classe como a seguinte, não há problema.
template <typename X, typename Y>
struct Foo<X, Y, void > { enum { value = 2 }; };
Por que essa classe não causa ambiguidade enquanto os outros fazem? Os dois não são a mesma coisa para as aulas com um valor verdadeiro ::? De qualquer forma, quaisquer dicas sobre o que estou fazendo de errado são apreciadas.
Obrigado pelas respostas, meu verdadeiro problema (para fazer com que o compilador selecione minha primeira especialização) foi resolvido substituindo struct Foo<X, Base<X>, void>
com struct Foo<X, Base<X>, typename enable_if< Base<X> >::type >
o que parece funcionar da maneira que eu quero.
Solução
A essência da sua pergunta é que você tem:
template <typename X, typename Y, typename Z>
struct Foo {};
template <typename X>
struct Foo<X, Base<X>, void> {}; // #1
template <typename X, typename Y>
struct Foo<X, Y, typename whatever<Y>::type> {}; // #2
E você está tentando combinar com isso
Foo<int, Base<int>, void>
Obviamente, ambas as especializações correspondem (a primeira com X = int
, o segundo com X = int, Y = Base<int>
).
De acordo com o padrão, a seção 14.5.4, se houver mais especializações correspondentes, uma ordem parcial (conforme definida em 14.5.5.2) é construída e a mais especializada é usada. No seu caso, no entanto, nenhum deles é mais especializado que o outro. (Simplificando, um modelo é mais especializado que outro, se você pode substituir cada parâmetro de tipo do último modelo por algum tipo e em resultado, obtenha a assinatura do primeiro. Além disso, se você tiver whatever<Y>::type
e você substitui Y
com Base<X>
você fica whatever<Base<X> >::type
não void
, ou seja, não há processamento realizado.)
Se você substituir #2
com
template <typename X, typename Y>
struct Foo<X, Y, void > {}; // #3
Em seguida, o conjunto do candidato contém novamente os dois modelos, no entanto, o número 1 é mais especializado do que o #3 e, como tal, é selecionado.
Outras dicas
Você não está perdendo um
<
símbolo?
Eu acho que você está perdendo um '<', um modelo deve parecer:
template< typename T >
struct myStruct
{};
//OR
template< class T >
struct myStruct
{};