Diferença de comportamento de resolução de sobrecarga entre o GCC e o clang (SFINAE)

O GCC aceita o seguinte código:

template <typename T>
struct meta
{
    typedef typename T::type type;
};

struct S {};

template <typename T>
typename meta<T>::type foo(T, S);

int foo(int, int);      

int main()
{
    foo(0, 0);
}

Mas o clang rejeita-o com o seguinte erro:

test.cpp:4:22: error: type 'int' cannot be used prior to '::' because it has no members
    typedef typename T::type type;
                     ^
test.cpp:10:10: note: in instantiation of template class 'meta<int>' requested here
typename meta<T>::type foo(T, S);
         ^
test.cpp:10:24: note: while substituting deduced template arguments into function template 'foo' [with T = int]
typename meta<T>::type foo(T, S);
                       ^

Isso parece sugerir uma diferença na ordem em que o GCC e o clang fazem determinadas operações durante a resolução de sobrecarga. O GCC parece rejeitar o modelo candidato devido à incompatibilidade de tipos no segundo parâmetro (S vs.int) antes tentando instanciar o tipo de retorno do candidato do modelo, enquanto o clang parece fazer o contrário.

Quem está certo?

Acredito que esta questão tenha implicações importantes para os autores de bibliotecas de modelos. Especificamente, se o clang estiver certo, o autor do modelofoo teria que fazer um trabalho extra para transformar o erro em uma falha de substituição.

EDITAR: Observe que o seguinte exemplo, um pouco mais simples, é rejeitado pelo GCC e pelo clang, com erros semelhantes:

template <typename T>
struct meta
{
    typedef typename T::type type;
};

template <typename T>
typename meta<T>::type foo(T);

int foo(int);      

int main()
{
    foo(0);
}

sugerindo que o GCC saiba que "somente tipos e expressões inválidos no contexto imediato do tipo de função e seus tipos de parâmetro de modelo podem resultar em uma falha de dedução". A diferença entre este exemplo e o original é a presença do segundo parâmetro de função no exemplo original, com base no qual o GCC lança o modelo de candidato antes mesmo de tentar executar a substituição no tipo de retorno. Eu acho que a questão é, se o GCC está correto para fazer as coisas nessa ordem, ou deveria estar tentando realizar uma substituição no tipo de retorno antes de considerar as correspondências nos tipos de argumento.

ATUALIZAR: A resposta de Luc Danton me convenceu de que o clang está correto ao rejeitar o código. Eu tenho de acordoarquivado um bug do GCC.

questionAnswers(2)

yourAnswerToTheQuestion