C++ #define 值未在源文件中定义

C++ #define value not defined in source file

我在主文件中定义了一个值,然后包含了一个头文件。定义的值未在源文件中定义。

Main.cpp:

#define TN_VALUE double
#include "SomeFile.hpp"

int main()
{
  TN_VALUE x = 3;
  func(x);
  return 0;
}

SomeFile.hpp:

#ifndef _SOME_FILE
#define _SOME_FILE
#ifndef TN_VALUE
#define TN_VALUE float
#endif
void func(TN_VALUE); // TN_VALUE = double
#endif

SomeFile.cpp:

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

void func(TN_VALUE _value) // TN_VALUE = float
{
  std::cout << _value << std::endl;
}

我们可以使用 g++-E 标志调查预处理代码的样子:

Stop after the preprocessing stage; do not run the compiler proper. The output is in the form of preprocessed source code, which is sent to the standard output.

(source)

这是 Main.cpp 的预处理代码:

$ g++ -E Main.cpp 
# 1 "Main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "Main.cpp"

# 1 "SomeFile.hpp" 1





void func(double);
# 3 "Main.cpp" 2

int main()
{
  double x = 3;
  func(x);
  return 0;
}

这是 SomeFile.cpp 的预处理代码,如果我们删除 #include <iostream>:

$ g++ -E SomeFile.cpp
# 1 "SomeFile.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.cpp"
# 1 "SomeFile.hpp" 1





void func(float);
# 2 "SomeFile.cpp" 2


void func(float _value)
{
  std::cout << _value << std::endl;
}

我们可以看到,在Main.cpp中,TN_VALUEdouble,因此我们得到void func(double);double x = 3;。在SomeFile.cpp中,TN_VALUEfloat。这是为什么?

这一切都与

有关
#ifndef TN_VALUE
#define TN_VALUE float
#endif

SomeFile.hpp 中。这告诉预处理器,如果未定义 TN_VALUE,请将 TN_VALUE 设置为 float。如果定义了 TN_VALUE,什么也不做(保留 TN_VALUE 的值。

Main.cpp中,你有

#define TN_VALUE double
#include "SomeFile.hpp"

这里的顺序意味着 TN_VALUE 将在预处理器到达 SomeFile.hpp 中的 ifndef 之前定义,因此 TN_VALUE 将保留 double值。

然后在 SomeFile.cpp 中,您立即有 #include "SomeFile.hpp"。由于 SomeFile.cpp 是独立于 Main.cpp 的翻译单元,因此未定义 TN_VALUE。这意味着,由于当预处理器到达 SomeFile.hpp 中的 ifndef 时未设置 TN_VALUE,预处理器将到达 #define TN_VALUE float 行,这将导致它设置 TN_VALUEfloat.

更多g++ -E分析

如果这是一个更大更复杂的代码库,我们可能希望通过仅查看 SomeFile.hpp 和使用不同定义生成的预处理输出来更仔细地隔离问题。

我们可以查看 SomeFile.hpp 没有定义的预处理输出:

$ g++ -E SomeFile.hpp
# 1 "SomeFile.hpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.hpp"





void func(float);

我们也可以使用g++-D标志来定义。如果我们使用 -D TN_VALUE=double,我们应该看到匹配 Main.cpp 情况的内容:

$ g++ -D TN_VALUE=double -E SomeFile.hpp
# 1 "SomeFile.hpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "SomeFile.hpp"





void func(double);

我们已经从上面的分析中预料到了这些结果。但是,这表明我们可以根据不同定义的各种值测试任何 C 或 C++ 头文件生成的预处理代码。

我假设你的问题是,
为什么 TN_VALUEMain.cpp 中是 double 而在 SomeFile.cpp 中是 float

对于那个问题,这是解释,
在 C++ 中,当您包含头文件时,它的内容会被复制到源文件中。这意味着 Main.cpp 文件中将有一个 SomeFile.hpp 文件的副本,而 SomeFile.cpp 文件中将有另一个副本。所以现在当编译器运行源文件时,在一个文件中 TN_VALUE 的定义是 float (在 SomeFile.cpp 中)并且它的值是 double (因为你已经定义它明确地)在另一个文件中(Main.cpp)。
这意味着将有两个不同的(重载的)函数。一个是 void func(float) 另一个是 void func(double).

由于函数定义存在于 SomeFile.cpp 文件中,因此编译时不会出现问题,但 Main.cpp 文件会出现问题,因为编译器说有一个函数linker 无法找到的 void func(double) 签名(因为这样的函数未编译)。这将触发 link 错误。

要解决此问题,您可以在头文件本身中定义函数并完全放弃 SomeFile.cpp 文件。这样,函数定义将放在 Main.cpp 文件中,TN_VALUE 的定义将解析为 double。之后此函数将不会出现 linking 错误。