13

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?

Community
  • 1
  • 1
ikh
  • 10,119
  • 1
  • 31
  • 70
  • 3
    clang++ rejects the `typename`. – dyp Sep 20 '14 at 00:26
  • 5
    I cared too much for my sanity to answer this on the other question... The Standard says in [namespace.udecl]/1 "If a *using-declaration* names a constructor, it implicitly declares a set of constructors in the class in which the *using-declaration* appears; otherwise the name specified in a *using-declaration* is a synonym for the name of some entity declared elsewhere." But in [class.ctor]/1 "Constructors do not have names." – dyp Sep 20 '14 at 00:32
  • 1
    Note that there's [namespace.udecl]/20 "If a *using-declaration* uses the keyword `typename` and specifies a dependent name, the name introduced by the *using-declaration* is treated as a *typedef-name*." – dyp Sep 20 '14 at 00:39
  • @dyp Oh, copy-and-paste makes the same typo;; Anyway, it seems that I and clang are correct; gcc seems to have some bugs on detail standard. – ikh Sep 20 '14 at 00:43
  • 1
    The rule is that "In a lookup in which function names are not ignored and the *nested-name-specifier* nominates a class C: — if the name specified after the *nested-name-specifier*, when looked up in C, is the *injected-class-name* of C (Clause 9) [...] the name is instead considered to name the constructor of class C. " ([class.qual]/p2) – T.C. Sep 20 '14 at 02:07

2 Answers2

3

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++
};
user1978011
  • 3,419
  • 25
  • 38
  • Thanks for your answer to my old unanswered question! The problem was solved, but I cannot help wondering *why* standard allow the *typename_opt* inside `using` declaration - it seems to be unnecessary, I think :) – ikh Apr 30 '15 at 10:51
  • 1
    Sorry, but this doesn't make any sense. The fact that the grammar allows the `typename` keyword for non-types is simply because the restriction that `typename` is used for types is a semantic restriction rather than a syntactic one. (But if you also address the semantic restrictions, if any, this could make for a good answer.) –  Apr 30 '15 at 11:37
  • @hvd I added some examples showing the use of typename for non-template base classes. Imho this is a syntactic question, not semantic. – user1978011 Apr 30 '15 at 12:08
  • 1
    It doesn't make sense to consider the syntax without considering the semantics as well. `void main(int arg, char *arg[-1]) { return "Hello, world!" % 1.23; }` is syntactically valid, but I don't think anyone would claim it's "standard conformant" because of that. –  May 01 '15 at 07:36
  • Nope, `using Base< T >::Ttype;` is issued to be an error in [Clang](https://godbolt.org/z/eM74KEMr4) – xmh0511 Aug 04 '21 at 05:16
  • In addition, I think `using Base< T >::Ttype;` has no problem here. The standard only says *If a using-declarator uses the keyword typename and specifies a dependent name ([temp.dep]), the name introduced by the using-declaration is treated as a typedef-name.* In other words, the keyword `typename` used in using-declarator indicates the name refers to a type. If we didn't use the keyword, Is there any rule be violated here? – xmh0511 Aug 04 '21 at 05:52
0

Sorry, why do you want using, why not just typedef?

template <typename T>
class Sub : public Base<T>
{
    typedef Base<T> Base;
};
depperm
  • 10,606
  • 4
  • 43
  • 67
Nickolay Merkin
  • 2,673
  • 1
  • 16
  • 14