C++03 3.2.2
...如果对象或非重载函数的名称出现在可能已评估的表达式中,则使用该函数。如果它不是纯粹的,则使用虚拟成员函数...
然后在3.2.3
中我们有: 每个程序应该只包含该程序中使用的每个非内联函数或对象的一个定义;无需诊断。该定义可以在程序中明确显示,可以在标准或用户定义的库中找到,或者(在适当的时候)隐式定义(见12.1,12.4和12.8)。
内联函数应在每个使用它的翻译单元中定义。
我正在阅读:不使用纯虚函数。 ODR仅适用于使用的功能。这不意味着以下是合法的吗?我猜答案是否定的,但事实并非如此,我无法理解为什么。
//x.h
struct A
{
virtual void f() = 0;
};
//y.cpp
#include "x.h"
void A::f()
{
}
//z.cpp
#include "x.h"
#include <iostream>
void A::f()
{
std::cout << "Hello" << std::endl;
}
//main.cpp
#include "x.h"
struct B:A
{
virtual void f()
{
A::f();
}
};
int main()
{
A* p = new B;
p->f();
}
答案 0 :(得分:11)
这两个条款并不相互排斥。
如果虚函数不纯,则使用虚函数并不意味着反过来。如果虚函数是纯函数,则并不意味着它必然不被使用。它可能仍在“如果其名称出现在可能的评估表达式中”中使用,例如在您的示例中:A::f();
。
答案 1 :(得分:3)
此代码违反ODR。 A :: f是多重定义的。因此它有UB。
翻译单元的多个定义仅允许按照$ 3.2 / 5
进行以下操作可以有多个定义 类的类型(第9条), 枚举类型(7.2),内联 具有外部联动功能 (7.1.2),课堂模板(第14条), 非静态函数模板(14.5.5), 类模板的静态数据成员 (14.5.1.3),类的成员函数 模板(14.5.1.1)或模板 专门化为哪些模板 参数未指定(14.7, 14.5.4)在程序中规定每个定义出现在不同的中 翻译单位,并提供 定义满足以下要求 要求。
答案 2 :(得分:1)
正如@Charles Bailey指出的那样,你的A::f
实际上已被使用,即使它是纯虚拟的。但这不是主要观点。
单一定义规则不适用于未使用的功能,这是不准确的。我们有:
3.2p1任何翻译单元都不得包含任何变量,函数,类类型,枚举类型或模板的多个定义。
3.2p3每个程序应该只包含该程序中使用的每个非内联函数或对象的一个定义;无需诊断。
这些要求似乎意味着使用的函数必须只有一个定义,而未使用的函数(包括从未显式调用的纯虚函数)可能没有定义或单个定义。在任何一种情况下,非内联函数的多个定义都会使程序格式不正确。
至少,我很确定这是意图。但是你可能会在措词上留下一个漏洞,因为一个非常文字的读物并没有说明不同翻译单元中同一个未使用函数的多个不同定义是不正确的。
// x.cpp
void f() {}
void g() {}
// y.cpp
#include <iostream>
void f() {
std::cout << "Huh" << std::endl;
}
void h() {}
// z.cpp
void g();
void h();
int main() {
g();
h();
return 0;
}
答案 3 :(得分:1)
这是相关的,但是偏离主题:从引用来看,标准中似乎存在一个漏洞:它还应该说使用纯虚拟析构函数,并且必须定义它;至少是否存在任何被销毁的派生类对象或者如果定义了这样的析构函数,因为派生类析构函数必须调用基本析构函数,隐式地使用qualified :: id语法。这种析构函数的定义通常是微不足道的,但不能被省略,也不能生成。
答案 4 :(得分:0)
[class.abstract]:“只有在使用(12.4)调用qualified-id语法(5.1)时才需要定义纯虚函数。”
A::f
已调用B::f
,因此A::f
必须有一个定义。