minha pergunta hoje é muito simples: por que o compilador não pode inferir parâmetros de modelo de construtores de classe, tanto quanto pode fazer a partir de parâmetros de função? Por exemplo, por que o código a seguir não poderia ser válido:
template<typename obj>
class Variable {
obj data;
public: Variable(obj d)
{
data = d;
}
};
int main()
{
int num = 2;
Variable var(num); //would be equivalent to Variable<int> var(num),
return 0; //but actually a compile error
}
Como eu disse, entendo que isso não é válido, então minha pergunta é por que não é? Permitir isso criaria grandes lacunas sintáticas? Existe uma instância em que não se desejaria essa funcionalidade (onde inferir um tipo causaria problemas)? Estou apenas tentando entender a lógica por trás da permissão de inferência de template para funções, mas não para classes construídas adequadamente.
template<class T> Variable<T> make_Variable(T&& p) {return Variable<T>(std::forward<T>(p));}
Respostas:
Acho que não é válido porque o construtor nem sempre é o único ponto de entrada da classe (estou falando sobre o construtor de cópia e operador =). Suponha que você esteja usando sua classe assim:
Não tenho certeza se seria tão óbvio para o analisador saber que tipo de modelo é MyClass pm;
Não tenho certeza se o que eu disse faz sentido, mas fique à vontade para adicionar algum comentário, essa é uma pergunta interessante.
C ++ 17
É aceito que C ++ 17 terá dedução de tipo a partir de argumentos do construtor.
Exemplos:
Artigo aceito .
fonte
MyClass *pm
aqui seria inválido pelo mesmo motivo que uma função declaradatemplate <typename T> void foo();
não pode ser chamada sem especialização explícita.Você não pode fazer o que pede por motivos que outras pessoas abordaram, mas você pode fazer o seguinte:
que para todos os efeitos e propósitos é a mesma coisa que você pede. Se você adora encapsulamento, pode tornar make_variable uma função de membro estático. Isso é o que as pessoas chamam de construtor nomeado. Portanto, ele não apenas faz o que você deseja, mas é quase chamado do que você deseja: o compilador está inferindo o parâmetro do modelo a partir do construtor (nomeado).
NB: qualquer compilador razoável irá otimizar o objeto temporário quando você escrever algo como
fonte
auto v = make_variable(instance)
isso sem realmente precisar especificar o tipostatic
membro ... pense nisso por apenas um segundo. Deixando isso de lado: as funções make livres foram de fato a solução, mas é um monte de boilerplate redundante, enquanto você está digitando, você sabe que não deveria, porque o compilador tem acesso a todas as informações que você está repetindo. .. e felizmente C ++ 17 canoniza isso.Na era iluminada de 2016, com dois novos padrões em nosso currículo desde que essa pergunta foi feita e um novo chegando, o crucial é saber que os compiladores que suportam o padrão C ++ 17 compilarão seu código como está .
Dedução de argumento de modelo para modelos de classe em C ++ 17
Aqui (cortesia de uma edição de Olzhas Zhumabek da resposta aceita) está o artigo detalhando as mudanças relevantes no padrão.
Lidando com as preocupações de outras respostas
A resposta com melhor classificação atual
Esta resposta indica que "construtor de cópia e
operator=
" não saberia as especializações de modelo corretas.Isso é um absurdo, porque o construtor de cópia padrão
operator=
existe apenas para um tipo de modelo conhecido :Aqui, como observei nos comentários, não há razão para
MyClass *pm
ser uma declaração legal com ou sem a nova forma de inferência:MyClass
não é um tipo (é um modelo), portanto, não faz sentido declarar um ponteiro de tipoMyClass
. Esta é uma maneira possível de corrigir o exemplo:Aqui, já
pm
é do tipo correto e, portanto, a inferência é trivial. Além disso, é impossível misturar tipos acidentalmente ao chamar o construtor de cópia:Aqui,
pm
haverá um ponteiro para uma cópia dem
. Aqui,MyClass
está sendo construído a partir dem
- que é do tipoMyClass<string>
(e não do tipo inexistenteMyClass
). Assim, no ponto em quepm
's tipo é inferido, não é suficiente informação para saber que o tipo de moldem
, e portanto, o tipo de modelo depm
, éstring
.Além disso, o seguinte sempre gerará um erro de compilação :
Isso ocorre porque a declaração do construtor de cópia não é modelada:
Aqui, o tipo de modelo do argumento do construtor de cópia corresponde ao tipo de modelo da classe geral; isto é, quando
MyClass<string>
é instanciado,MyClass<string>::MyClass(const MyClass<string>&);
é instanciado com ele, e quandoMyClass<int>
é instanciado,MyClass<int>::MyClass(const MyClass<int>&);
é instanciado. A menos que seja explicitamente especificado ou um construtor com modelo seja declarado, não há razão para o compilador instanciarMyClass<int>::MyClass(const MyClass<string>&);
, o que obviamente seria inapropriado.A resposta de Cătălin Pitiș
Pitiș dá um exemplo deduzindo
Variable<int>
eVariable<double>
, em seguida, afirma:Conforme observado no exemplo anterior,
Variable
ele próprio não é um nome de tipo, embora o novo recurso faça com que ele se pareça sintaticamente.Pitiș então pergunta o que aconteceria se nenhum construtor fosse fornecido que permitiria a inferência apropriada. A resposta é que nenhuma inferência é permitida, porque a inferência é disparada pela chamada do construtor . Sem uma chamada de construtor, não há inferência .
Isso é semelhante a perguntar qual versão de
foo
é deduzida aqui:A resposta é que esse código é ilegal, pelo motivo declarado.
Resposta de MSalter
Esta é, pelo que eu posso dizer, a única resposta que levanta uma preocupação legítima sobre o recurso proposto.
O exemplo é:
A questão principal é: o compilador seleciona o construtor inferido por tipo aqui ou o construtor de cópia ?
Experimentando o código, podemos ver que o construtor de cópia está selecionado. Para expandir o exemplo :
Não tenho certeza de como a proposta e a nova versão da norma especificam isso; parece ser determinado por "guias de dedução", que são um novo padrão de linguagem que ainda não entendo.
Também não sei por que a
var4
dedução é ilegal; o erro do compilador de g ++ parece indicar que a instrução está sendo analisada como uma declaração de função.fonte
var4
é apenas um caso de "análise mais irritante" (não relacionado à dedução do argumento do modelo). Costumávamos usar apenas parênteses extras para isso, mas atualmente acho que usar colchetes para denotar construção sem ambigüidades é o conselho usual.Variable var4(Variable(num));
é tratado como uma declaração de função? Em caso afirmativo, por queVariable(num)
uma especificação de parâmetro válida?Ainda ausente: torna o código a seguir bastante ambíguo:
fonte
Supondo que o compilador suporte o que você pediu. Então este código é válido:
Agora, eu tenho o mesmo nome de tipo (Variável) no código para dois tipos diferentes (Variável e Variável). Do meu ponto de vista subjetivo, isso afeta muito a legibilidade do código. Ter o mesmo nome de tipo para dois tipos diferentes no mesmo namespace parece enganoso para mim.
Atualização posterior: Outra coisa a considerar: especialização de modelo parcial (ou total).
E se eu me especializar em Variável e não fornecer nenhum construtor como você espera?
Então eu teria:
Então eu tenho o código:
O que o compilador deve fazer? Use a definição de classe de Variável genérica para deduzir que é Variável e descubra que Variável não fornece um construtor de parâmetro.
fonte
Os padrões C ++ 03 e C ++ 11 não permitem a dedução do argumento do modelo a partir dos parâmetros passados para o construtor.
Mas existe uma proposta para "Dedução de parâmetros de modelo para construtores", então você pode obter o que está pedindo em breve. Editar: na verdade, esse recurso foi confirmado para C ++ 17.
Consulte: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html e http://www.open-std.org/jtc1/sc22/wg21/docs/ papers / 2015 / p0091r0.html
fonte
Muitas classes não dependem dos parâmetros do construtor. Existem apenas algumas classes que têm apenas um construtor e parametrizam com base no (s) tipo (s) deste (s) construtor (es).
Se você realmente precisa de inferência de modelo, use uma função auxiliar:
fonte
A dedução de tipos é limitada às funções de modelo no C ++ atual, mas há muito se percebeu que a dedução de tipos em outros contextos seria muito útil. Conseqüentemente, C ++ 0x's
auto
.Embora exatamente o que você sugere não seja possível em C ++ 0x, o seguinte mostra que você pode chegar bem perto:
fonte
Você está certo, o compilador poderia adivinhar facilmente, mas não está no padrão ou C ++ 0x até onde eu sei, então você terá que esperar pelo menos mais 10 anos (padrões ISO taxa fixa de retorno) antes que os fornecedores de compiladores adicionem este recurso
fonte
Vejamos o problema com referência a uma classe com a qual todos deveriam estar familiarizados - std :: vector.
Em primeiro lugar, um uso muito comum de vetor é usar o construtor que não leva parâmetros:
Nesse caso, obviamente, nenhuma inferência pode ser realizada.
Um segundo uso comum é criar um vetor pré-dimensionado:
Aqui, se a inferência fosse usada:
obtemos um vetor de ints, não de strings, e presumivelmente não é dimensionado!
Por último, considere os construtores que usam vários parâmetros - com "inferência":
Qual parâmetro deve ser usado para inferência? Precisaríamos de alguma forma de dizer ao compilador que deveria ser o segundo.
Com todos esses problemas para uma classe tão simples quanto vetorial, é fácil ver por que a inferência não é usada.
fonte
Fazendo do ctor um modelo, a variável pode ter apenas uma forma, mas vários ctors:
Vejo? Não podemos ter vários membros Variable :: data.
fonte
Consulte The C ++ Template Argument Deduction para obter mais informações sobre isso.
fonte