8

这是一个带有未定义方法的类。似乎编译器允许构造此类的实例,只要从未调用未定义的成员函数:

struct A {
    void foo();
};

int main() {
    A a;      // <-- Works in both VC2013 and g++
    a.foo();  // <-- Error in both VC2013 and g++
}

这是一种类似的情况,但涉及继承。子类Bar扩展基类FooFoo定义一个方法g()Bar声明了同名方法但没有定义它:

#include <iostream>

struct Foo {
    void g() { std::cout << "g\n"; }
};

struct Bar : Foo {
    void g();
};

int main() {
    Bar b;      // Works in both VC2013 and g++
    b.Foo::g(); // Works in both VC2013 and g++
    b.g();      // Error in both VC2013 and g++
}

这是上述的一种变体。这里唯一的区别g()virtual两者FooBar

#include <iostream>

struct Foo {
    virtual void g() { std::cout << "g\n"; }
};

struct Bar : Foo {
    virtual void g();
};

int main() {
    Bar b;      // Works in g++. But not in VC2013, which gives
                // 'fatal error LNK1120: 1 unresolved externals'

    b.Foo::g(); // Works in g++, but VC2013 already failed on b's construction
    b.g();      // Error in g++, but VC2013 already failed on b's construction
}

有关 VC2013 和 g++ 之间不同行为的对比,请参见代码注释。

  1. 哪个编译器是正确的,如果有的话?
  2. 为什么VC2013的编译器有virtual关键字的版本和没有关键字的版本有一些不同的抱怨virtual
  3. 是否总是允许未使用的未定义方法?如果不是,在哪些情况下是不允许的?
  4. 即使不提供定义Bar, 的声明是否也g()算作覆盖?Bar
4

1 回答 1

8

哪个编译器是正确的,如果有的话?

他们都是对的。您的代码错误,无需诊断。[class.virtual]/11

在类中声明的虚函数应在该类中定义或声明为纯(10.4),或两者兼而有之;但不需要诊断(3.2)。

[intro.compliance]/2:

如果一个程序违反了不需要诊断的规则,则本国际标准对该程序的实现没有要求。

查看您的 GCC 优化设置,它们可能会影响行为。


是否总是允许未使用的未定义方法?

当且仅当它被 odr 使用时,必须定义成员函数。[basic.def.odr]/3:

每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断。

现在考虑 [basic.def.odr]/2:

一个表达式可能被求值,除非它是一个未求值的操作数(第 5 条)或其子表达式。
[…]
如果不是纯的,则虚拟成员函数是 odr-used 的。
名称显示为潜在求值表达式或一组候选函数的成员的非重载函数,如果在从潜在求值表达式中引用时通过重载决议选择,则为 odr-used,除非它是纯虚函数函数及其名称没有明确限定。

decltype您仍然可以在or中使用未定义的非虚拟成员函数sizeof。但是非纯虚函数之所以被 ODR 使用,仅仅是因为它们不是纯虚函数。


即使 Bar 没有提供定义,Bar 的 g() 声明是否也算作覆盖?

是的。

于 2014-11-07T22:54:50.817 回答