当 C++ 程序在两个地方定义时,如何阻止它从错误的源文件调用函数体?
How do I stop a C++ program from calling a function body from the wrong source file, when it's defined in 2 places?
我试过用同一函数的两个副本创建一个静态库,定义在两个单独的 header/source 文件中。
这 2 个源文件应该是互斥的,并且不能包含在同一文件中。
我用下面的示例代码重现了这个问题。
这是在 VS2019 上编写和测试的,我还没有在任何其他编译器上测试过。
以下是foobar静态库:
foo.h
#pragma once
#ifdef BAR
static_assert(-1, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
void foobar();
foo.cpp
#include "foo.h"
#include <iostream>
void foobar(){
std::cout << "this is foo!";
}
bar.h
#pragma once
#ifdef FOO
static_assert(-1, "Foo is already included, you shouldn't include both foo & bar");
#endif //FOO
#define BAR
void foobar();
bar.cpp
#include "bar.h"
#include <iostream>
void foobar(){
std::cout << "this is bar!";
}
然后我可以用一个 main.cpp
创建一个可执行项目
我创建了这个文件的 3 个不同版本,但它们都打印同一行 "this is foo!"
以下是主文件的 3 个版本:
#include "bar.h"
int main() {
foobar();
}
#include "foo.h"
int main() {
foobar();
}
#include "foo.h"
#include "bar.h"
int main() {
foobar();
}
所以我的问题是:
- 为什么可执行文件总是打印
"this is foo!"
,不管它包含什么?
- 尽管头文件中有 static_assert,但当
foo.h
和 bar.h
包含在同一个源文件中时,为什么编译不会失败?
- 有没有办法在同一个库中实现同一个函数的 2 个定义,但不允许同时调用它们?
编辑:
非常感谢@selbie,你的回答很有用。我已经重写了它,这样它就不那么骇人听闻了。
foo.h
#pragma once
#ifdef BAR
static_assert(false, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
#include "foobar.h"
bar.h
#pragma once
#ifdef FOO
static_assert(false, "Foo is already included, you shouldn't include both foo & bar");
#endif //FOO
#define BAR
#include "foobar.h"
foobar.h
#pragma once
void foobar_foo();
void foobar_bar();
inline void foobar() {
#ifdef FOO
foobar_foo();
#endif // FOO
#ifdef BAR
foobar_bar();
#endif // BAR
}
foobar.cpp
#include "foobar.h"
#include <iostream>
void foobar_foo() {
std::cout << "this is foo!";
}
void foobar_bar() {
std::cout << "this is bar!";
}
我很惊讶链接器没有就此向您发出警告。当它将最终的可执行程序链接在一起时,它将选择 foobar
实现之一并丢弃另一个。
预处理器破解。 foobar
定义为调用唯一函数名称的宏或内联函数。定义 foo.h 和 foo.cpp 如下所示:
foo.h
#pragma once
#ifdef BAR
static_assert(false, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
void foobar_foo();
#ifndef FOOBAR_IMPL
inline void foobar() {foobar_foo();}
#endif
// OR
// #ifdef foobar
// #undef foobar
// #endif
// #define foobar foobar_foo
然后foo.cpp:
#define FOOBAR_IMPL // avoids the ODR issue that user17732522 called out in comments
#include "foo.h"
#include <iostream>
void foobar_foo(){
std::cout << "this is foo!";
}
对 bar.h 和 bar.cpp 重复此模式。
我试过用同一函数的两个副本创建一个静态库,定义在两个单独的 header/source 文件中。 这 2 个源文件应该是互斥的,并且不能包含在同一文件中。 我用下面的示例代码重现了这个问题。 这是在 VS2019 上编写和测试的,我还没有在任何其他编译器上测试过。
以下是foobar静态库:
foo.h
#pragma once
#ifdef BAR
static_assert(-1, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
void foobar();
foo.cpp
#include "foo.h"
#include <iostream>
void foobar(){
std::cout << "this is foo!";
}
bar.h
#pragma once
#ifdef FOO
static_assert(-1, "Foo is already included, you shouldn't include both foo & bar");
#endif //FOO
#define BAR
void foobar();
bar.cpp
#include "bar.h"
#include <iostream>
void foobar(){
std::cout << "this is bar!";
}
然后我可以用一个 main.cpp
创建一个可执行项目我创建了这个文件的 3 个不同版本,但它们都打印同一行 "this is foo!"
以下是主文件的 3 个版本:
#include "bar.h"
int main() {
foobar();
}
#include "foo.h"
int main() {
foobar();
}
#include "foo.h"
#include "bar.h"
int main() {
foobar();
}
所以我的问题是:
- 为什么可执行文件总是打印
"this is foo!"
,不管它包含什么? - 尽管头文件中有 static_assert,但当
foo.h
和bar.h
包含在同一个源文件中时,为什么编译不会失败? - 有没有办法在同一个库中实现同一个函数的 2 个定义,但不允许同时调用它们?
编辑:
非常感谢@selbie,你的回答很有用。我已经重写了它,这样它就不那么骇人听闻了。
foo.h
#pragma once
#ifdef BAR
static_assert(false, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
#include "foobar.h"
bar.h
#pragma once
#ifdef FOO
static_assert(false, "Foo is already included, you shouldn't include both foo & bar");
#endif //FOO
#define BAR
#include "foobar.h"
foobar.h
#pragma once
void foobar_foo();
void foobar_bar();
inline void foobar() {
#ifdef FOO
foobar_foo();
#endif // FOO
#ifdef BAR
foobar_bar();
#endif // BAR
}
foobar.cpp
#include "foobar.h"
#include <iostream>
void foobar_foo() {
std::cout << "this is foo!";
}
void foobar_bar() {
std::cout << "this is bar!";
}
我很惊讶链接器没有就此向您发出警告。当它将最终的可执行程序链接在一起时,它将选择 foobar
实现之一并丢弃另一个。
预处理器破解。 foobar
定义为调用唯一函数名称的宏或内联函数。定义 foo.h 和 foo.cpp 如下所示:
foo.h
#pragma once
#ifdef BAR
static_assert(false, "Bar is already included, you shouldn't include both foo & bar");
#endif //BAR
#define FOO
void foobar_foo();
#ifndef FOOBAR_IMPL
inline void foobar() {foobar_foo();}
#endif
// OR
// #ifdef foobar
// #undef foobar
// #endif
// #define foobar foobar_foo
然后foo.cpp:
#define FOOBAR_IMPL // avoids the ODR issue that user17732522 called out in comments
#include "foo.h"
#include <iostream>
void foobar_foo(){
std::cout << "this is foo!";
}
对 bar.h 和 bar.cpp 重复此模式。