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_VALUE
是double
,因此我们得到void func(double);
和double x = 3;
。在SomeFile.cpp
中,TN_VALUE
是float
。这是为什么?
这一切都与
有关
#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_VALUE
到 float
.
更多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_VALUE
在 Main.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 错误。
我在主文件中定义了一个值,然后包含了一个头文件。定义的值未在源文件中定义。
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_VALUE
是double
,因此我们得到void func(double);
和double x = 3;
。在SomeFile.cpp
中,TN_VALUE
是float
。这是为什么?
这一切都与
有关#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_VALUE
到 float
.
更多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_VALUE
在 Main.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 错误。