【问题标题】:Why do I receive a syntax error during compilation?为什么在编译过程中会收到语法错误?
【发布时间】:2019-11-03 05:13:55
【问题描述】:

有人可以解释为什么我在构建过程中收到语法错误:标识符“Bar”,而foo.hpp 头文件中没有包含class Bar;

在构建之前,我在 Visual Studio 2019 中没有收到任何错误,构建顺序似乎是 bar,然后是 foo,然后是 main,因此遵循 #include 语句看起来好像在构建过程中,bar 头文件首先包含在 foo 头中。

我在下面包含了概述基本问题的代码。

//Foo header file
#pragma once

#include "bar.hpp"  
#include <iostream>

class Bar; //Commenting this line out results in no longer being able to build the project

class Foo {

public:
    Foo();

    void pickSomething(Bar& bar);
};

//Foo cpp file
#include "foo.hpp"

Foo::Foo() {
    std::cout << "Made Foo" << std::endl;
}

void Foo::pickSomething(Bar& bar) {
    bar.getSomething();
    std::cout << "Picked something!" << std::endl;
}

//Bar header file
#pragma once

#include "foo.hpp"
#include <iostream>

class Foo;

class Bar {

public:
    Bar(Foo& foo);

    void getSomething();
};

//Bar cpp file
#include "bar.hpp"

Bar::Bar(Foo& foo) {
    std::cout << "Made bar" << std::endl;
}

void Bar::getSomething() {
    std::cout << "Gave something!" << std::endl;
}

//main file
#include "foo.hpp"
#include "bar.hpp"

int main() {
    Foo foo;
    Bar bar(foo);
    foo.pickSomething(bar);

    return 0;
}

【问题讨论】:

  • 您是否在构建期间收到这些错误,或者它们在代码侧边栏中突出显示?
  • 我只在构建期间收到它们。
  • 看来你有一个循环包含。如果 file2 包含 file1,则 file1 不能包含 file2。
  • 假设这两个类以原始代码的方式相互依赖,那么组织文件的正确方法是什么? foo 和 bar 是否应该在单个标题中定义?不知何故,这感觉像是一种糟糕的做法。
  • 参见this answer 以了解非常相似的案例。

标签: c++ include


【解决方案1】:

总结:

  • foo.hpp 包括 bar.hppbar.hpp 包括 foo.hpp。这是一个循环依赖。
  • #pragma once 确保不会再次加载相同的标头(如果已加载)。

  • 只有bar.cpp编译失败(foo.cppmain.cpp会编译成功)

让我们在编译bar.cpp的时候跟随预处理器。事情按以下顺序发生。

  1. bar.cpp 包括 bar.hpp
  2. bar.hpp 包括 foo.hpp。 (注意以下两点)
    • 预处理器记住它输入了bar.hpp,并将避免在当前循环中再次输入它。
    • 符号Bar(类Bar)仍未加载,因为之前包含foo.hpp
  3. foo.hpp 尝试包含 bar.hpp
    • 预处理器知道它已经输入了bar.hpp,因此它被忽略了!
    • 但是,Foo 类声明使用一个名为 Bar 的符号。但这不是之前声明的!

未知符号Bar 可以代表很多东西(类、宏等)。所以编译器(正确地)失败了,因为它不知道如何处理它。

解决方案是前向声明。在那里,您向编译器保证符号 Bar 代表一个类。只要您不做任何编译器需要了解类布局的事情(例如定义Bar 类型的成员函数、访问Bar 的成员等...),编译就会成功。

【讨论】:

  • 出于好奇,如果编译器确实需要了解类布局,那么最好的补救措施是什么?
  • IMO,这需要由设计级别处理,因为它可能表明存在设计问题。但请注意,如果您使用指针成员,前向声明就足够了。取消引用指针时,您只需要源文件中的完整定义。所以通常这样的问题可以通过这种方式轻松解决。
【解决方案2】:

foo.hppbar.hpp 不相互依赖,因此前向声明是避免其头文件之间循环依赖的好方法。只有.cpp 文件实际上需要包含这两个头文件。

【讨论】:

    猜你喜欢
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-09-04
    • 2022-01-23
    • 1970-01-01
    • 2023-03-05
    • 2021-12-27
    • 1970-01-01
    相关资源
    最近更新 更多