c++0x inherited constructor in templates

3.1k views Asked by At

Here is class foo:

template <typename T>
struct foo
{
    foo()
    {
        t = nullptr;
    }

    foo(T* p, bool flag)
    {
        t = p;
    }
private:
    T* t;
};

Here is class bar:

template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo<T>;
};

Is it correct syntax for inheriting constructors? If I use "using foo::foo;" then compiler of Visual C++ 2010 dies. So basically how to inherit constructors from template classes in VC++ 2010?

4

There are 4 answers

0
Johannes Schaub - litb On BEST ANSWER
template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo<T>;
};

To let this parse correctly, you would need to insert template before the foo<T>;, to tell the compiler that foo is to be regarded as a template name (it cannot look into foo<T> to tell itself, since T is unknown). But using ::template is not allowed in a using declaration. The name also does not refer to all constructors of bar: Instead, it would refer to a specific constructor function template specialization (T is the template argument) of such a constructor as follows

template<typename T>
foo();

In addition, it's not valid for a using declaration to use a template-id (like foo<T>) as its name (which in effect forbids it to refer to function template specialization, with the addition of forbidding to name conversion function template specializations stated too), so even if you correct the parsing problem using ::template (if it would be possible), you would still error out at this point.

When inherited constructors were introduced, special rules were added that allow to reference a constructor using a syntactic rule: If you have a qualified-id (which basically a qualified name using ...::...), and the last qualified before the final part names a specific class, then you can denote the constructor(s) of that class in two additional ways:

  • If the class was named using a template-id (a name of the form foo<T>) and the final part matches the template name (so, foo<T>::foo or TTP<T>::TTP with TTP being a template template parameter).
  • If the final part matches the class name (so, foo::foo or T::T, with T being a template parameter).

These two additional rules are only active in a using declaration. And they were naturally not present in C++03. The other rule that was also present in C++03 is: If the final part names the injected class name, then this qualified name also refers to the constructor:

  • foo::foo would therefor work. But with this rule alone, T::T (where T denotes class foo) would not work, because foo has no member called T.

Therefor, with the special rules in place you can write

using foo<T>::foo;
using bar::foo::foo; // valid too

The second is valid too: foo is the injected class name which was injected into the base class foo<T> and inherited to bar. We refer to that name by bar::foo, and then add the last part foo, which refers to the injected class name again, to denote the constructor(s) of `foo.

Now you understand why the initial name you tried would refer to a constructor function template specialization (if it were to be allowed to): Because the foo<T>::foo part would name all constructors, and the <T> that would follow would then filter out the template and pass the type argument.

5
lurscher On

you don't need the second template parameter;

template <typename T>
struct bar: public foo<T>
{
    using foo<T>::foo;
};

should do

edit i withdraw that this works on g++-4.4.1, however this should be the correct syntax when the feature becomes available

6
In silico On

The other answers have already done a good job of explaining how inheriting constructors in C++0x work. However, as of this writing, no compiler has completely implemented the entire C++0x feature set. Unfortunately that means VC++ 2010 does not yet support inheriting constructors.

The C++0x standard has not yet been published. The final draft of the standard will be finished sometime in March, but it'll take a few more months for ISO to publish it. During that time, compiler writers are rolling out features so they'll be as C++0x compliant as possible when the standard is finalized.

I believe the latest version of GCC supports inheriting constructors, so if you must try it out now, you can use that. Of course, C++0x support is experimental and subject to change as bugs are found, etc.

1
Howard Hinnant On

If your compiler doesn't yet support inheriting constructors, but does support variadic macros, variadic templates and rvalue references, and a really handy type_trait, here's a really decent workaround:

#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';
}

If you don't have is_constructible yet, the basic idea works without it, but the "inherited constructor" will be overly greedy.