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 является экспериментальной и может изменяться по мере обнаружения ошибок и т. Д.

Другие вопросы по тегам