朋友类,继承和typedef - 哪种行为是正确的?

时间:2016-10-05 13:35:40

标签: c++ gcc clang

我偶然发现了一个错误,该错误仅出现在GCC 6.2.0上,而不是出现在Clang 3.9.0上(均为-std=c++14模式)。我不确定哪种行为是正确的(以及我是否应该提交错误)。

以下是代码:

template<typename type_t>
class foo_t
{
};

class bar_t
{
public:
    using foo_t = int;
};

class baz_t:
    public bar_t
{
private:
    template<typename type_t>
    friend class foo_t;
};

在GCC上,这会出现以下错误:

test.cpp:17:15: error: using typedef-name ‘using foo_t = int’ after ‘class’
  friend class foo_t;
               ^~~~~
test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here
  using foo_t = int;
                   ^

根据我所知的C ++标准,父typedef(或using s)不应泄漏到子的范围内,您需要明确限定名称:例如参见{{ 3}}。所以在我看来GCC在这里是不正确的,但我不太确定我的C ++知识可以放心地说。

感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

  

根据我所知的C ++标准,父typedef(或using s)不应该泄漏到孩子的范围内,你需要明确限定名称

这是不正确的。在基类中声明的成员(包括类型别名)通常在派生类中可见。您链接的问题专门处理具有依赖基类的模板,其中两阶段名称查找适用(并且同样适用于所有内容,而不仅仅是类型别名)。

除此之外,标准的相关部分是C ++ 14(N4140)[dcl.type.elab] 7.1.6.3/2(强调我的):

  

3.4.4描述了如何对 elaborated-type-specifier中的标识符进行名称查找。如果标识符   解析为类名枚举名称, elaborated-type-specifier 将其引入声明中   同样,简单类型说明符引入了类型名称。 如果标识符解析为 typedef-name 或    simple-template-id 解析为别名模板特化, elaborated-type-specifier 格式不正确。

(注意: elaborated-type-specifier 是我们正在处理的某些class T的构造T

反过来说,3.4.4说明在将详细类型说明符中的标识符解析为名称时,将忽略非类型名称(但通常会找到类型名称)。

因此,GCC实际上是正确的,因为范围foo_t中的typedef-name bar_t是&#34;更近&#34;在范围内,而不是全局范围模板名称foo_t。因此,foo_t中的非限定名称baz_t会解析为bar_t::foo_t,这是 typedef-name ,因此会使详细类型说明符格式错误。< / p>

问题在于不合格名称foo_t的解析。正如您在对该问题的评论中注意到的那样,明确说明您的foo_t应该解决问题:

tempalte <typename type_t>
friend class ::foo_t;

答案 1 :(得分:1)

会员&#39;声明对其子类可见。您的问题的问题是重复声明。请检查以下代码

template<typename type_t>
class foo_t
{
};

class bar_t
{
public:
    using foo_t = int;
};

class baz_t :
    public bar_t
{
public:
    foo_t val; // Here, the foo_t refers to the int
};

int main()
{
    baz_t().val; // this works
}

您发布的错误代码test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here已经提到了这个问题。

另一方面,朋友类不能应用于简单数据类型,请检查以下代码:

using foo_t = int;

class bar_t
{
public:

};

class baz_t :
    public bar_t
{
public:
    template<typename type_t>
    friend class foo_t; 
    //error: using typedef-name ‘using foo_t = int’ after ‘class’
};

如果您确实需要具有重复的声明名称,那么这是解决方法:

template<typename type_t>
class foo_t
{
};

class bar_t
{
public:
    using foo_t = int;
};

class baz_t:
    public bar_t
{
public:
    template<typename type_t>
    friend class ::foo_t;   // The global scope

    foo_t val; // The int
};