101

这只是在另一个问题的背景下提出的。

显然,类模板中的成员函数只有在使用 ODR 时才会被实例化。有人可以解释这到底是什么意思。关于单一定义规则(ODR)的维基百科文章没有提到“ ODR-use ”。

但是标准将其定义为

名称显示为潜在求值表达式的变量是odr-used的,除非它是一个满足出现在常量表达式 (5.19) 中的要求并且立即应用左值到右值转换 (4.1) 的对象。

在 [basic.def.odr] 中。

编辑:显然这是错误的部分,整个段落包含不同事物的多个定义。这可能与类模板成员函数相关:

名称显示为潜在求值表达式或一组候选函数的成员的非重载函数,如果在从潜在求值表达式中引用时通过重载决议选择,则为 odr-used,除非它是纯虚函数函数及其名称没有明确限定。

但是我不明白,这条规则如何在多个编译单元中起作用?如果我显式实例化类模板,是否所有成员函数都实例化了?

4

2 回答 2

77

它只是一个任意定义,标准使用它来指定何时必须为实体提供定义(而不仅仅是声明)。该标准并不仅仅说“使用”,因为这可以根据上下文进行不同的解释。并且某些 ODR 使用并不真正对应于通常与“使用”相关联的内容。例如,一个虚函数总是被 ODR 使用,除非它是纯的,即使它实际上并没有在程序的任何地方被调用。

完整的定义在第3.2节第二段中,尽管其中包含对其他部分的引用以完成定义。

关于模板,使用 ODR 只是问题的一部分;另一部分是实例化。特别是,§14.7 涵盖了何时实例化模板。但两者是相关的:虽然第 14.7.1 节(隐式实例化)中的文本相当长,但基本原则是模板只有在使用时才会被实例化,在这种情况下,使用意味着 ODR 使用。因此,类模板的成员函数只有在被调用时才会被实例化,或者如果它是虚拟的并且类本身被实例化。标准本身在很多地方都依赖于这一点:单个元素的std::list<>::sort使用<,但您可以在不支持的元素类型上实例化一个列表<,只要您不调用sort它。

于 2013-10-28T09:31:59.840 回答
31

简而言之,odr-used 意味着某些东西(变量或函数)在必须存在其定义的上下文中使用。

例如,

struct F {
   static const int g_x = 2;
};

int g_x_plus_1 = F::g_x + 1; // in this context, only the value of g_x is needed.
                             // so it's OK without the definition of g_x

vector<int>  vi;
vi.push_back( F::g_x );      // Error, this is odr-used, push_back(const int & t) expect
                             // a const lvalue, so it's definition must be present

注意,上面的push_back是在MSVC 2013中传入的,这个行为不符合标准,gcc 4.8.2和clang 3.8.0都失败了,错误信息是:undefined reference toF::g_x

于 2017-08-08T00:58:20.183 回答