【问题标题】:C++ using declaration with typename in inheriting-constructorsC++ 在继承构造函数中使用带有类型名的声明
【发布时间】:2014-11-14 15:26:48
【问题描述】:

在阅读this question时,我发现了一个奇怪的地方:

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

由于typenameBaseclass&lt;T&gt;::Baseclass 应该是injected class name,而不是构造函数。据我所知,和这个是一样的情况:

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 *)

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

【问题讨论】:

  • clang++ 拒绝typename
  • 我太在意自己的理智,无法回答另一个问题...标准在 [namespace.udecl]/1 中说:“如果 using-declaration 名称构造函数,它在出现 using-declaration 的类中隐式声明一组构造函数;否则,在 using-declaration 中指定的名称是名称的同义词在别处声明的某个实体。”但在 [class.ctor]/1 “构造函数没有名称。”
  • 注意这里有 [namespace.udecl]/20 "如果一个using-declaration 使用关键字typename 并指定一个从属名称,则由using-declaration 被视为 typedef-name。"
  • @dyp 哦,复制粘贴也是一样的错字;;反正我和clang好像是对的; gcc 似乎在详细标准上有一些错误。
  • 规则是“在不忽略函数名并且 nested-name-specifier 指定一个类 C 的查找中:- 如果在 nested-name-specifier,当在 C 中查找时,是 C 的 injected-class-name(第 9 条)[...] 该名称被认为是命名C 类的构造函数。" ([class.qual]/p2)

标签: c++ language-lawyer typename using-declaration inheriting-constructors


【解决方案1】:

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

使用声明:

使用 typename_opt 嵌套名称说明符 unqualified-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&lt; T &gt;::Ttype; 中缺少typename。这显然是一个 clang 错误。


编辑 如果基类不是模板类,也可以使用 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 声明中使用 typename_opt - 我认为这似乎是不必要的 :)
  • 抱歉,这没有任何意义。语法允许typename 关键字用于非类型的事实仅仅是因为typename 用于类型的限制是语义限制而不是句法限制。 (但如果你还解决了语义限制,如果有的话,这可能是一个很好的答案。)
  • @hvd 我添加了一些示例,展示了对非模板基类使用 typename。恕我直言,这是一个句法问题,而不是语义问题。
  • 不考虑语义就考虑语法是没有意义的。 void main(int arg, char *arg[-1]) { return "Hello, world!" % 1.23; } 在语法上是有效的,但我认为没有人会因此声称它“符合标准”。
  • 不,using Base&lt; T &gt;::Ttype;Clang 中发出错误
【解决方案2】:

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

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

【讨论】:

  • 你应该搜索“c++继承构造函数”:)
猜你喜欢
  • 1970-01-01
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-20
相关资源
最近更新 更多