clang或gcc在这个内部类成员访问方面正确吗?

5
以下代码在clang上编译并运行正常,但在gcc上会出现“error: invalid use of non-static data member ‘Outer::a’”的错误:
#include <functional>
#include <vector>
#include <assert.h>
#include <iostream>
#include <memory>

class Outer
{
public:
    bool a = false;

    virtual void f() = 0;

    template <typename T>
    class Inner : public T
    {
    public:
        virtual void f() override
        {
            a = true; // Note: accessed through inheritance, not through outer scope
        }
    };
};

struct Foo : Outer { };

int main()
{
    Outer::Inner<Foo> f;
    f.f();
}

将"this->a"添加到内部类中可以使其在两个编译器上运行,但我想知道正确的行为和标准对此的规定。

有趣的是,上述代码在VS2017的其他代码库中正常工作,但当我在家中使用VS2017进行隔离测试时,它会像GCC一样失败并出现相同的错误。

您可以在此处尝试编译:


3
相关链接:https://dev59.com/gW445IYBdhLWcg3w6eNo为什么我必须通过this指针访问模板基类成员? - NathanOliver
2
也许将其中一个标签替换为“language-lawyer”标签。 - 463035818_is_not_a_number
2
应该是CLang上的bug。我不明白它怎么可能不是编译错误。 - SergeyA
1
@NathanOliver 啊,这不是我第一次被这个问题困扰了。看起来gcc是正确的,那么问题是为什么clang允许它呢?你链接中的示例并没有。 - monoceres
我明白clang在做什么,它正在执行以下操作:https://rextester.com/FRQ46060 -- 它查找a,找到了Outer::a。如果您将a = 7替换为Outer::a = 7;,那么gcc也会编译它。我想知道(a) Outer::a = 7;是否也是非法的,如果不是,它与a = 7有何不同。 - Yakk - Adam Nevraumont
显示剩余2条评论
1个回答

4

这段代码存在问题,但是不需要进行诊断。因此,Gcc是正确和友好的。而Clang和MSVC缺少诊断只是编译器质量问题。

涉及到的标准规则是[temp.res]/8:

在任何实例化之前都可以检查模板的有效性。 [注:知道哪些名称是类型名称允许以这种方式检查每个模板的语法。 — end note  ] 如果程序满足以下条件,则形式不正确,不需要进行诊断:

  • [..]

  • 由于与模板参数无关的构造,或者[…],一个模板的假设实例化紧随其定义之后将是不正确的。

在函数体中,未限定的id表达式a不依赖于任何模板参数,因此应在模板定义点解析此id表达式,而不需要任何模板参数的知识。在这一点上,这个表达式是不正确的。


注意:非限定的id表达式(类成员访问之外)表达式仅在它命名该类或非依赖基类的成员时才应该是成员[temp.dep.type]/5:

如果一个名称:

  • 是未限定的名称,当查找时,它引用当前实例化或非依赖基类之一的类的至少一个成员。

@Yakk-AdamNevraumont,我看到的链接代码与问题中的代码完全相同。 - Oliv
抱歉,链接已修复。 - Yakk - Adam Nevraumont
@Yakk-AdamNevraumont 我找到了规则:temp.dep.type/5 - Oliv
Outer::a 难道不是 temp.dep.type/6 吗?那么 a 也应该同样处理吧? - Yakk - Adam Nevraumont
不,a不是一个合格的标识符! - Yakk - Adam Nevraumont
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接