4

我刚刚读到 constexpr 和 inline 函数遵循单一定义规则,但它们的定义必须相同。所以我尝试一下:

inline void foo() {
    return;
}

inline void foo() {
    return;
}

int main() {
    foo();
};

错误:重新定义“void foo()”,
以及

constexpr int foo() {
    return 1;
}

constexpr int foo() {
    return 1;
}

int main() {
    constexpr x = foo();
}; 

错误:重新定义“constexpr int foo()”

那么究竟是什么意思,constexpr 和 inline 函数可以服从 ODR 呢?

4

3 回答 3

3

在一个翻译单元中重复定义函数。这总是被禁止的:

任何翻译单元都不得包含一个以上的任何变量、函数、类类型、枚举类型或模板的定义。(C++11 3.2/1)

对于inline函数,您可以在多个翻译单元中以完全相同的方式定义相同的函数(读取:.cpp 文件)。事实上,您必须在每个翻译单元中定义它(通常通过在头文件中定义它来完成):

内联函数应在使用它的每个翻译单元中定义。(C++11 3.2/3)

对于具有外部链接(非静态)函数的“正常”(非内联、非 constexpr、非模板等)函数,这通常会(不需要诊断)导致链接器错误。

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

总结一下:

  • 永远不要在一个翻译单元(这是一个 .cpp 文件和所有直接或间接包含的标题)中多次定义任何内容。
  • 您可以将一定数量的东西放入头文件中,它们将在几个不同的翻译单元中包含一次,例如:
    • inline功能
    • class类型和templates
    • statica 的数据成员class template
于 2015-03-26T19:43:09.060 回答
3

我刚刚读到 constexpr 和 inline 函数遵循单一定义规则,但它们的定义必须相同。

这是指不同翻译单元中的内联函数。在您的示例中,它们都在同一个翻译单元中。

这在C++ 标准草案 3.2一定义规则[basic.def.odr]中涵盖,其中说:

类类型(第 9 条)、枚举类型(7.2)、带外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数模板(14.5.6)可以有多个定义、类模板的静态数据成员 (14.5.1.3)、类模板的成员函数 (14.5.1.1) 或在程序中未指定某些模板参数的模板特化 (14.7、14.5.5),前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求。给定这样一个名为 D 的实体在多个翻译单元中定义,则

并包括以下项目符号:

  • D 的每个定义都应由相同的记号序列组成;和
于 2015-03-26T19:42:16.837 回答
2

如果你有:

文件 1.cpp:

inline void foo() { std::cout << "Came to foo in file1.cpp" << std::endl; }

文件 2.cpp:

inline void foo() { std::cout << "Came to foo in file2.cpp" << std::endl; }

并且你将这些文件链接到一个可执行文件中,你就违反了,one-definition-rule因为inline函数的两个版本不同。

于 2015-03-26T19:54:15.307 回答