C++0x унаследованный конструктор в шаблонах
Вот класс foo:
template <typename T>
struct foo
{
foo()
{
t = nullptr;
}
foo(T* p, bool flag)
{
t = p;
}
private:
T* t;
};
Вот класс бар:
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
Это правильный синтаксис для наследования конструкторов? Если я использую "using foo::foo;" затем компилятор Visual C++ 2010 умирает. Так в основном, как наследовать конструкторы от шаблонных классов в VC++ 2010?
4 ответа
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
Чтобы этот синтаксический анализ был корректным, вам необходимо вставить template
перед foo<T>;
сказать компилятору, что foo
должен рассматриваться как имя шаблона (он не может foo<T>
сказать себе, так как T
неизвестно). Но используя ::template
не допускается в объявлении об использовании. Имя также не относится ко всем конструкторам bar
Вместо этого он будет ссылаться на конкретную специализацию шаблона функции конструктора (T
является аргументом шаблона) такого конструктора следующим образом
template<typename T>
foo();
Кроме того, недопустимо использование объявления template-id
(лайк foo<T>
) как его имя (которое фактически запрещает ссылаться на специализацию шаблонов функций, с добавлением запрета на указанную спецификацию шаблонов функций преобразования имен), так что даже если вы исправите проблему синтаксического анализа, используя ::template
(если бы это было возможно), вы все равно ошиблись бы на этом этапе.
Когда были введены унаследованные конструкторы, были добавлены специальные правила, которые позволяют ссылаться на конструктор с помощью синтаксического правила: если у вас есть квалифицированный идентификатор (который в основном является квалифицированным именем, использующим ...::...
), а последняя квалифицируется перед тем, как последняя часть назовет конкретный класс, тогда вы можете обозначить конструктор (ы) этого класса двумя дополнительными способами:
- Если класс был назван с использованием идентификатора шаблона (имя формы
foo<T>
) и последняя часть соответствует имени шаблона (так,foo<T>::foo
или жеTTP<T>::TTP
сTTP
будучи параметром шаблона шаблона). - Если последняя часть соответствует имени класса (так,
foo::foo
или жеT::T
, сT
будучи параметром шаблона).
Эти два дополнительных правила активны только в объявлении использования. И они, естественно, не присутствовали в C++03. Другое правило, которое также присутствовало в C++ 03: если последняя часть именует имя внедренного класса, то это квалифицированное имя также ссылается на конструктор:
foo::foo
будет для этого работать. Но только с этим правиломT::T
(гдеT
обозначает классfoo
) не будет работать, потому чтоfoo
не имеет ни одного членаT
,
Для этого вы можете написать специальные правила.
using foo<T>::foo;
using bar::foo::foo; // valid too
Второе тоже верно: foo
это имя внедренного класса, которое было введено в базовый класс foo<T>
и наследуется bar
, Мы называем это имя bar::foo
, а затем добавить последнюю часть foo
, который снова ссылается на введенное имя класса, чтобы обозначить конструктор (ы) `foo.
Теперь вы понимаете, почему исходное имя, которое вы пробовали, будет ссылаться на специализацию шаблона функции конструктора (если это будет разрешено): foo<T>::foo
часть будет называть все конструкторы, а <T>
затем будет отфильтрован шаблон и передан аргумент типа.
Если ваш компилятор еще не поддерживает наследующие конструкторы, но поддерживает переменные макросы, переменные шаблоны и ссылки на значения, и действительно удобный type_trait, вот действительно достойный обходной путь:
#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';
}
Если у вас еще нет is_constructible, основная идея работает без него, но "унаследованный конструктор" будет слишком жадным.
Вам не нужен второй параметр шаблона;
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo;
};
следует сделать
Отредактирую, что это работает на g++-4.4.1, однако это должен быть правильный синтаксис, когда функция станет доступной
Другие ответы уже проделали хорошую работу, объясняя, как работают наследующие конструкторы в C++0x. Однако на момент написания этой статьи ни один компилятор не реализовал полностью весь набор функций C++0x. К сожалению, это означает, что VC++ 2010 еще не поддерживает наследование конструкторов.
Стандарт C++0x еще не опубликован. Окончательный проект стандарта будет закончен где-то в марте, но для публикации ISO потребуется еще несколько месяцев. В течение этого времени разработчики компиляторов разворачивают функции, поэтому они будут максимально совместимы с C++0x, когда стандарт будет завершен.
Я полагаю, что последняя версия GCC поддерживает наследующие конструкторы, поэтому, если вам нужно попробовать это сейчас, вы можете использовать это. Конечно, поддержка C++0x является экспериментальной и может изменяться по мере обнаружения ошибок и т. Д.