C++在继承构造函数中使用具有typename的声明

C++ using declaration with typename in inheriting-constructors

本文关键字:typename 声明 继承 构造函数 C++      更新时间:2023-10-16

在阅读这个问题时,我发现了一个奇怪的点:

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

由于typenameBaseclass<T>::Baseclass应该是注入的类名,而不是构造函数。据我所知,情况与此相同:

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

为了确保安全,我写了一个测试代码。

#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");
}

但是,它在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 *)

它也在没有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 *)

为什么我得到这些结果?我错过了什么?

标准对此非常清楚([namespace.udcl]/1)

使用声明:

使用typename_opt嵌套名称说明符非限定id;

因此,typename关键字是using声明的可选部分,即使对于非类型的using声明也可能出现该部分。因此,以下代码应符合标准:

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

SubNoTypenameSubTypename都被gcc认定为符合标准。另一方面,clang++在SubTypename中抱怨typename关键字不正确。然而,这甚至不一致,因为它应该抱怨using Base< T >::Ttype;中缺少typename。这显然是一个叮当作响的bug。


编辑如果基类不是模板类,typename关键字也是允许的,在这个地方通常你永远不会期望这个关键字是有效的:

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++
};

对不起,为什么要using,为什么不只是typedef

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