Construtor herdado c ++ 0x em modelos
-
29-10-2019 - |
Pergunta
Aqui está a classe foo:
template <typename T>
struct foo
{
foo()
{
t = nullptr;
}
foo(T* p, bool flag)
{
t = p;
}
private:
T* t;
};
Aqui está o bar da turma:
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
É a sintaxe correta para herdar construtores?Se eu usar "using foo :: foo;"em seguida, o compilador do Visual C ++ 2010 morre. Então, basicamente, como herdar construtores de classes de modelo no VC ++ 2010?
Solução
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
Para permitir que isso analise corretamente, você precisaria inserir template
antes do foo<T>;
, para dizer ao compilador que foo
deve ser considerado como um nome de modelo (ele não pode olhar em foo<T>
para dizer a si mesmo, uma vez que T
é desconhecido). Mas o uso de ::template
não é permitido em uma declaração de uso. O nome também não se refere a todos os construtores de bar
: em vez disso, ele se referia a uma especialização de modelo de função de construtor específica (T
é o argumento do modelo) de tal construtor, como segue
template<typename T>
foo();
Além disso, não é válido para uma declaração de uso usar um template-id
(como foo<T>
) como seu nome (o que na verdade proíbe que se refira à especialização do modelo de função, com a adição de proibir a conversão de nomes de especializações de modelo de função declarada ), portanto, mesmo que você corrija o problema de análise usando ::template
(se for possível), ainda haverá um erro neste ponto.
Quando os construtores herdados foram introduzidos, regras especiais foram adicionadas que permitem fazer referência a um construtor usando uma regra sintática: se você tiver um id qualificado (que basicamente é um nome qualificado usando ...::...
) e o último qualificado antes dos nomes das partes finais uma classe específica, então você pode denotar o (s) construtor (es) dessa classe de duas maneiras adicionais:
- Se a classe foi nomeada usando um template-id (um nome do formulário
foo<T>
) e a parte final corresponde ao nome do modelo (então,foo<T>::foo
ouTTP<T>::TTP
comTTP
sendo um parâmetro de modelo de modelo). - Se a parte final corresponder ao nome da classe (então,
foo::foo
ouT::T
, comT
sendo um parâmetro de modelo).
Essas duas regras adicionais só estão ativas em uma declaração de uso. E eles naturalmente não estavam presentes no C ++ 03. A outra regra que também estava presente em C ++ 03 é: Se a parte final nomear o nome da classe injetada, então este nome qualificado também se refere ao construtor:
-
foo::foo
funcionaria, portanto. Mas com esta regra sozinha,T::T
(ondeT
denota a classefoo
) não funcionaria, porquefoo
não tem membro chamadoT
.
Portanto, com as regras especiais em vigor, você pode escrever
using foo<T>::foo;
using bar::foo::foo; // valid too
A segunda também é válida: foo
é o nome da classe injetada que foi injetada na classe base foo<T>
e herdada por bar
. Referimo-nos a esse nome por bar::foo
e, em seguida, adicionamos a última parte foo
, que se refere ao nome da classe injetada novamente, para denotar o (s) construtor (es) de `foo.
Agora você entende por que o nome inicial que tentou se referir a uma especialização de modelo de função de construtor (se fosse permitido): Porque a parte foo<T>::foo
nomearia todos os construtores, e o <T>
que se seguiria filtraria o modelo e passe o argumento de tipo.
Outras dicas
Se o seu compilador ainda não suporta construtores herdados, mas suporta macros variadic, templates variadic e referências rvalue, e um type_trait muito útil, aqui está uma solução realmente decente:
#include <type_traits>
#include <utility>
#include <ostream>
enum Color {Red, Blue};
#define USING(Derived, Base) \
template<typename ...Args, \
typename = typename std::enable_if \
< \
std::is_constructible<Base, Args...>::value \
>::type> \
Derived(Args &&...args) \
: Base(std::forward<Args>(args)...) { } \
template<typename Mixin>
class add_color
: public Mixin
{
Color color;
public:
USING(add_color, Mixin);
friend std::ostream& operator<<(std::ostream& os, const add_color& x)
{
switch (x.color)
{
case Red:
os << "Red";
break;
case Blue:
os << "Blue";
break;
}
os << ' ' << x.first << ' ' << x.second;
return os;
}
};
#include <string>
#include <iostream>
int main()
{
add_color<std::pair<std::string, int>> x1("five", 5);
std::cout << "x1 = " << x1 << '\n';
add_color<std::pair<std::string, int>> x3;
std::cout << "x3 = " << x3 << '\n';
add_color<std::pair<std::string, int>> x4 = x1;
std::cout << "x4 = " << x4 << '\n';
std::pair<std::string, int> p;
add_color<std::pair<std::string, int>> x5 = p;
std::cout << "x5 = " << x5 << '\n';
}
Se você ainda não tem is_constructible, a ideia básica funciona sem ele, mas o "construtor herdado" será excessivamente ganancioso.
você não precisa do segundo parâmetro do modelo;
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo;
};
deve servir
editar eu retiro que isso funciona no g ++ - 4.4.1, no entanto, esta deve ser a sintaxe correta quando o recurso for disponibilizado
As outras respostas já explicaram como os construtores herdados em C ++ 0x funcionam.No entanto, até o momento em que este livro foi escrito, nenhum compilador implementou completamente todo o conjunto de recursos C ++ 0x.Infelizmente, isso significa que o VC ++ 2010 ainda não oferece suporte a construtores herdados.
O padrão C ++ 0x ainda não foi publicado.O rascunho final do padrão será concluído em algum momentoMarço , mas vai demorar mais alguns meses para a ISO publicá-lo.Durante esse tempo, os criadores do compilador estão lançando recursos para que sejam o mais compatíveis com C ++ 0x possível quando o padrão for finalizado.
Eu acredito que a versão mais recente do GCC suporta a herança de construtores, então se você deve tentar agora, você pode usar.Obviamente, o suporte a C ++ 0x é experimental e sujeito a alterações à medida que bugs forem encontrados, etc.