为什么一个定义规则,而不是一个声明规则?

时间:2017-10-28 09:15:57

标签: c++ c one-definition-rule

我已阅读以下材料:

https://www.wikiwand.com/en/One_Definition_Rule

http://en.cppreference.com/w/cpp/language/definition

What is the difference between a definition and a declaration?

但是,仍然无法弄清楚为什么它是一个定义规则而不是一个声明规则?

我认为声明是定义的一个子集,所以一个定义规则就足够了。

5 个答案:

答案 0 :(得分:7)

一个声明规则太严格,阻止编译多次使用相同标头的程序。这也使得无法使用反向引用来定义数据结构。

查看第一个点(使用标题)的一种简单方法是考虑由两个翻译单元A.cppB.cpp组成的程序,这两个翻译单元都包含<string>标题。

Header included twice

翻译单元A.cppB.cpp是独立翻译的。通过加入<string>,两个翻译单元都会获得std::string的声明。

关于第二点(具有反向引用的数据结构),考虑一个定义树的示例,其中每个节点都具有对其父树的反向引用:

// Does not compile
struct tree {
    struct node *root;
};
struct node {
    struct node *left;
    struct node *right;
    struct tree *owner;
};

此示例无法编译,因为来自node的{​​{1}}未声明。切换struct node *treestruct node声明的顺序不会有帮助,因为来自struct tree的{​​{1}}将是未声明的。 C和C ++中唯一的解决方案是为两个tree中的任何一个引入第二个声明。

答案 1 :(得分:6)

因为.h文件中的相同声明可能包含在多个编译单元中,并且因为多个定义肯定是编程错误,而多个声明不是。

答案 2 :(得分:4)

定义是声明的子集,而不是相反。每个定义都是一个声明,并且声明不是定义。

int i = 3;      // definition and declaration
extern int i;   // ok: (re)declaration
int i = 4;      // error: redefinition

extern int j;   // declaration
extern int j;   // ok: (re)declaration
int j = 5;      // ok: (re)declaration and definition
int j = 6;      // error: redefinition

答案 3 :(得分:2)

因为在头文件中声明了一个函数

// header toto.h
int f(void);

并且您希望在它所属的编译单元中定义它,您可以

#include "toto.h"

int f(void) {
   return 0;
}

该定义也是一个声明,因此该编译单元看到两个声明,一个在标题中,一个在.c.cpp文件中。

简而言之,多声明规则允许检查不同源文件之间的一致性。

答案 4 :(得分:1)

原因确实是C ++翻译模型可以轻松处理冲突的多个声明;它只需要工具集的编译器部分来检测源代码中的错误:

int X();
void X(); // error

编译器可以很容易地做到这一点。

如果在任何翻译单元中没有这样的错误,那么没有问题;每个翻译单元中的每个X()电话都是相同的;剩下要做的是链接器将每个调用链接到一个正确的目标。宣言已经完成了他们的工作,不再发挥作用。

现在有了多个定义,这并不容易。定义涉及多个翻译单元,超出了编译阶段的范围。

我们已经在上面的示例中看到了这一点。 X()电话已到位,但现在我们需要保证它们都在同一目的地,X()定义

可能只有一个这样的定义应该是明确的,但如何强制执行呢?简单来说,当它将目标代码链接在一起时,源代码已经处理完毕。

答案是C ++基本上选择给程序员带来负担。在大多数现实情况下,强制编译器/链接器实现者检查所有多个定义的相等性和检测差异将超出C ++工具集的功能,或完全打破这些工具的工作方式,因此实用的解决方案是禁止它和/或强迫程序员确保它们都是相同的,否则会得到不确定的行为。

相关问题