Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ using declaration with typename in inheriting-constructors

While reading this question, I found a strange point:

template <typename T>
class Subclass : public Baseclass<T>
{
public:
    using typename Baseclass<T>::Baseclass;
    //    ^^^^^^^^
};

Since typename, Baseclass<T>::Baseclass should be injected class name, not a constructor. As far as I know, it's the same case as this:

template <typename T>
class Base
{
public:
    typedef short some_type;
};

template <typename T>
class Sub : public Base<T>
{
public:
    using typename Base<T>::some_type;
};

To make sure, I wrote a test code.

#include <iostream>

template <typename T>
class Base
{
public:
    Base() { std::cout << "A::A()\n"; }
    Base(int) { std::cout << "A::A(int)\n"; }
    Base(const char *) { std::cout << "A::A(const char *)\n"; }
};

template <typename T>
class Sub : public Base<T>
{
    using typename Base<T>::Base;
};

int main()
{
    Sub<char> s1;
    Sub<char> s2(3);
    Sub<char> s3("asdf");
}

However, it runs on gcc 4.8.3.

$ g++ -std=c++1y -Wall -Wextra -Werror -pedantic test.cpp -o test && ./test
A::A()
A::A(int)
A::A(const char *)

It also runs without typename.

$ cat test.cpp
...
    using Base<T>::Base;
...

$ g++ -std=c++1y -Wall -Wextra -Werror -pedantic test.cpp -o test && ./test
A::A()
A::A(int)
A::A(const char *)

Why did I get these results? What did I miss?

like image 435
ikh Avatar asked Sep 20 '14 00:09

ikh


People also ask

How do you inherit constructors?

To inherit only selected ones you need to write the individual constructors manually and call the base constructor as needed from them. Historically constructors could not be inherited in the C++03 standard. You needed to inherit them manually one by one by calling base implementation on your own.

Does inheritance inherit constructors?

Constructors are not inherited. The superclass constructor can be called from the first line of a subclass constructor by using the keyword super and passing appropriate parameters to set the private instance variables of the superclass.

What is the difference between using declaration and using directive?

The obvious reason is that a using declaration and a using directive have different effects. A using declaration introduces the name immediately into the current scope, so using std::swap introduces the name into the local scope; lookup stops here, and the only symbol you find is std::swap .

How the constructors are invoked in inheritance?

For multiple inheritance order of constructor call is, the base class's constructors are called in the order of inheritance and then the derived class's constructor.


1 Answers

The standard is pretty clear about this ([namespace.udecl]/1)

using-declaration:

using typename_opt nested-name-specifier unqualified-id ;

The typename keyword is therefore an optional part of a using declaration that may appear even for using declarations of non-types. The following code should therefore be standard conformant:

template < typename T > class Base {
  protected:
    typedef T Ttype;
    Ttype member;

  public:
    Base() {
        std::cout << "A::A()\n";
    }
    Base(int) {
        std::cout << "A::A(int)\n";
    }
    Base(const char *) {
        std::cout << "A::A(const char *)\n";
    }

  protected:
    void memfunc(void) {
        std::cout << "A::memfunc(void)\n";
    }
};

template< typename T >
struct SubNoTypename : protected Base< T > {
    using Base< T >::Base;
    using Base< T >::member;
    using Base< T >::memfunc;
    using Base< T >::Ttype;  // n.b. no error in clang++
};

template< typename T >
struct SubTypename : protected Base< T > {
    using typename Base< T >::Base;    // error in clang++
    using typename Base< T >::member;  // error in clang++
    using typename Base< T >::memfunc; // error in clang++
    using typename Base< T >::Ttype;
};

Both SubNoTypename and SubTypename are recognized as standard conformant by gcc. On the other hand clang++ complains in SubTypename about malplaced typename keywords. However, this is not even consistent, because it should then complain about a missing typename in using Base< T >::Ttype;. This clearly is a clang bug.


Edit The typename keyword is also allowed if the base class is no template class, a place where ordinarily you would never expect this keyword to be valid:

class BaseNoTemplate {
  protected:
    typedef T Ttype;
    Ttype member;

  public:
    BaseNoTemplate() {
        std::cout << "A::A()\n";
    }
    BaseNoTemplate(const char *) {
        std::cout << "A::A(const char *)\n";
    }

    void memfunc(void) {
        std::cout << "A::memfunc(void)\n";
    }
};

struct SubNoTemplateNoTypename : protected BaseNoTemplate {
    using BaseNoTemplate::BaseNoTemplate;
    using BaseNoTemplate::member;
    using BaseNoTemplate::memfunc;
    using BaseNoTemplate::Ttype;
};

struct SubNoTemplateTypename : protected BaseNoTemplate {
    using typename BaseNoTemplate::BaseNoTemplate; // error in clang++
    using typename BaseNoTemplate::member;  // error in clang++
    using typename BaseNoTemplate::memfunc; // error in clang++
    using typename BaseNoTemplate::Ttype;   // n.b. no error in clang++
};
like image 71
user1978011 Avatar answered Oct 21 '22 04:10

user1978011