Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/155.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么声明/定义顺序在C++;?_C++_C++11 - Fatal编程技术网

C++ 为什么声明/定义顺序在C++;?

C++ 为什么声明/定义顺序在C++;?,c++,c++11,C++,C++11,现在,我在C++中多次遇到声明和定义顺序的问题: struct A { void Test() { B(); } }; void B() { A a; } 当然,这可以通过预先删除B()来解决。通常这足以解决这些问题。但是,当使用基于模块的仅头库或类似复杂的include系统时,这种声明/定义概念可能会非常痛苦。我在下面提供了一个简单的例子 现在大多数现代语言编译器对源文件进行两次遍历,第一次遍历构建声明,第二次遍历处理定义。在C++中引入这个方案也不应该破坏任何旧代码。所以

现在,我在C++中多次遇到声明和定义顺序的问题:

struct A {
    void Test() { B(); }
};

void B() {
    A a;
}
当然,这可以通过预先删除
B()
来解决。通常这足以解决这些问题。但是,当使用基于模块的仅头库或类似复杂的include系统时,这种声明/定义概念可能会非常痛苦。我在下面提供了一个简单的例子

现在大多数现代语言编译器对源文件进行两次遍历,第一次遍历构建声明,第二次遍历处理定义。在C++中引入这个方案也不应该破坏任何旧代码。所以,

    为什么没有这样或类似的方法已经被引入到C++中?
  • 现行标准中是否有禁止这种方法的相关条款

例子 这是一个基于模块的标头库的示例,由于缺少predeclarations,它具有阻塞include。要解决这个问题,库的用户必须预先声明“缺少的”类,这是不可行的。 当然,这个问题可以通过使用一个公共include头来解决,该头在定义之前对所有声明进行排序,但是使用两次传递,这段代码也可以工作,不需要修改

oom.h

#pragma once
#include "string.h"

struct OOM {
    String message;
};
string.h

#pragma once
#include "array.h"

struct String {
    Array data;
};
array.h

#pragma once

struct Array {
    void Alloc();
};

#include "oom.h"

void Array::Alloc() { throw OOM(); }
stru用法.cpp

#include "string.h"
int main() {
    String str;
}

< C++语法中有歧义,只有知道标识符是指什么才能解决。

例如:

a * b;
如果
a
是变量,则可以是乘法;如果
a
是类型,则可以是指针声明。每种方法都会导致不同的解析树,因此解析器必须知道
a
是什么


这意味着解析和解析不能在单独的传递中执行,但必须在一次传递中完成,导致预声明的要求。

< P>在C++文法中存在歧义,只有知道标识符是指的,才能解决。 例如:

a * b;
如果
a
是变量,则可以是乘法;如果
a
是类型,则可以是指针声明。每种方法都会导致不同的解析树,因此解析器必须知道
a
是什么


这意味着解析和名称解析不能在单独的过程中执行,而必须在一个过程中完成,因此需要预先声明名称。

一个更简单的解决方案是将类定义与函数定义分开:

struct A {
    void Test();
};

struct B {
    A a;
};

inline void A::Test() {
    B();
}

更简单的解决方案是将类定义与函数定义分开:

struct A {
    void Test();
};

struct B {
    A a;
};

inline void A::Test() {
    B();
}
g
当前调用
f(int)
,因为它是唯一可见的
f
。在你的世界里它叫什么

  • 如果它调用
    f(double)
    ,您就破坏了大量的现有代码
  • 如果您提出了一些规则使它仍然调用
    f(int)
    ,那么这意味着如果我编写

    void g2() { f2(3.14); }
    void f2(double);
    
    然后为参数引入一个更差的匹配-比如,
    void f2(int)
    g2
    之前,
    g2
    会突然开始调用错误的东西。那是一场噩梦

g
当前调用
f(int)
,因为它是唯一可见的
f
。在你的世界里它叫什么

  • 如果它调用
    f(double)
    ,您就破坏了大量的现有代码
  • 如果您提出了一些规则使它仍然调用
    f(int)
    ,那么这意味着如果我编写

    void g2() { f2(3.14); }
    void f2(double);
    
    然后为参数引入一个更差的匹配-比如,
    void f2(int)
    g2
    之前,
    g2
    会突然开始调用错误的东西。那是一场噩梦


我认为一次通过是有好处的。我可能出于偏见,但我发现需要两次通过(由程序员和编译器)的代码更难阅读。如果我在程序中发现循环DEP,我会发现一个典型的设计问题:-)我认为一次通过是有好处的,我可能出于偏见,但是我发现需要两个过程(程序员和编译器)的代码更难阅读。如果我在我的程序中发现循环DEP,我会发现一个典型的设计问题:-)难道不能通过延迟这些模糊性直到所有候选项都解决吗?当没有可能创建名为
a
的类型的“挂起声明”时,这并不含糊。我的意思是,这不像是用户通过预先声明从两种含义中选择一种。代码一开始并不含糊<代码>a*b完全由上下文定义。是的,它可以有两种含义,但上下文决定它是哪一种。@nyronium否。在更现代的语言中,第一个过程从代码构造一个抽象语法树(AST),第二个过程执行名称解析。但是在这里,您不能在进行名称解析之前构建AST,因为解析器缺少信息。上面显示的歧义意味着解析器将不知道如何继续解析其余的代码。难道不能通过延迟这些歧义直到所有候选代码都得到解决吗?当没有可能创建名为
a
的类型的“挂起声明”时,这并不含糊。我的意思是,这不像是用户通过预先声明从两种含义中选择一种。代码一开始并不含糊<代码>a*b完全由上下文定义。是的,它可以有两种含义,但上下文决定它是哪一种。@nyronium否。在更现代的语言中,第一个过程从代码构造一个抽象语法树(AST),第二个过程执行名称解析。但是在这里,您不能在进行名称解析之前构建AST,因为解析器缺少信息。上面显示的歧义意味着解析器不知道如何继续解析其余代码。