Qual compilador está correto para o seguinte comportamento de sobrecarga/especialização?
-
03-07-2019 - |
Pergunta
Considere o seguinte código:
#include <stdio.h>
namespace Foo {
template <typename T>
void foo(T *, int) { puts("T"); }
template <typename T>
struct foo_fun {
static void fun() { foo((T *)0, 0); };
};
}
namespace Foo {
void foo(int *, int) { puts("int"); }
}
using namespace Foo;
int main() {
foo_fun<int> fun;
fun.fun();
}
Qual é a saída esperada? "T" ou int?
Um compilador (GCC 4.0.1 da saída Xcode 3.1.2) da Apple "int", dois outros compiladores (GCC 4.1.2 e 4.1.3) saída "T".
Se eu mover foo (int *, int) declaração/definição antes da versão Foo (t *, int), toda saída "int". A ordem de sobrecarga/especialização neste caso é definida pelo padrão atual?
Solução
O segundo void foo(...
é uma sobrecarga (e não uma especialização) que não é visível na definição de foo_fun::fun
Portanto, não será encontrado no contexto da definição de modelo. Porque T*
é um tipo dependente, resolução de foo
na expressão foo((T*)0, 0)
será adiado até que o tempo de instanciação do modelo e o contexto da instanciação também sejam considerados. No entanto, 14.6.4.2 do padrão diz que se o nome da função for um ID não qualificado mas não um modelo-id Então, para a pesquisa não ADL, apenas as funções visíveis no ponto de definição do modelo são consideradas. Não há argumentos de função do Foo
namespace para que nenhuma pesquisa dependente de argumento ocorra, portanto a versão de modelo de foo
é chamado e não a sobrecarga que não é de templos.
Muito obrigado a Litb pelas correções dessa resposta.
Se você fez uma especialização como abaixo, como as especializações são escolhidas no tempo de instanciação do modelo, a especialização pode ser chamada desde que a especialização relevante seja visível no ponto em que o modelo de função é instanciado pela primeira vez para int
.
namespace Foo {
template<>
void foo<int>(int *, int) { puts("int"); }
}
Capítulo 14 do padrão atual, mas não é muito legível :)
Editar: se eu tivesse que escolher a parte mais relevante do padrão, provavelmente seria 14.6 [temp.res] parágrafo 9. (ligeiramente abreviado) se um nome não depender de um Parâmetro de modelo, uma declaração para esse nome estará em escopo no ponto em que o nome aparece na definição do modelo; O nome está vinculado à declaração encontrada nesse ponto e essa ligação não é afetada por declarações visíveis no ponto de instanciação.
Editar, editar: mas você também precisa levar em consideração 14.6.4.2 [temp.dep.candidate]. É muito difícil e perigoso tentar fazer referência ao padrão devido a todas as interdependências, essa resposta é um exemplo.
Outras dicas
Como regra geral, de duas versões de um compilador, o posterior é mais provável que seja mais padrão.