提问人:igormcoelho 提问时间:10/1/2023 更新时间:10/3/2023 访问量:104
C++20 LNK2019 MSVC 错误,而 C++17 有效
C++20 LNK2019 error with MSVC, while C++17 works
问:
我在使用 CMake 和 Bazel 构建的项目上遇到了 C++20 的问题,两者都适用于 linux(GCC 和 Clang)上的 c++17/c++20,但它们仅在带有 c++20 的 Windows msvc 上失败。该错误是链接错误 (LNK2019),这很奇怪,因为是 CMake(或 Bazel)自己构建了对象,但它们无法链接。 我在 github 上放了一些可重现的代码,尽管项目很大: https://github.com/manydeps/manydeps-cln 我尝试更改 MSVC 版本(github actions 为 windows-2022 / windows-latest 映像提供了 4 种不同的版本):14.16.27023、14.29.30133、14.35.32215 和 14.37.32822。 另一件奇怪的事情是,CMake 决定使用 14.35.32215,而 Bazel 使用 14.37.32822(即使删除文件夹,我也无法更改它)。 我正在将其构建为Windows上的静态/ MT库(以及Linux上的.a)。
这是CLN项目的奇怪链接错误:
[5 / 6] Linking app_demo_cln.exe; 1s local
ERROR: D:/a/manydeps-cln/manydeps-cln/BUILD.bazel:21:10: Linking app_demo_cln.exe failed: (Exit 1120): link.exe failed: error executing command (from target //:app_demo_cln) C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\bin\HostX64\x64\link.exe @bazel-out/x64_windows-fastbuild/bin/app_demo_cln.exe-2.params
cln.lib(cl_prin_globals.obj) : error LNK2019: unresolved external symbol "struct cln::cl_heap_string * __cdecl cl_make_heap_string(char const *)" (?cl_make_heap_string@@YAPEAUcl_heap_string@cln@@PEBD@Z) referenced in function "public: __cdecl cln::cl_string::cl_string(char const *)" (??0cl_string@cln@@QEAA@PEBD@Z)
Hint on symbols that are defined and could potentially match:
"struct cln::cl_heap_string * __cdecl cln::cl_make_heap_string(char const *)" (?cl_make_heap_string@cln@@YAPEAUcl_heap_string@1@PEBD@Z)
bazel-out\x64_windows-fastbuild\bin\app_demo_cln.exe : fatal error LNK1120: 1 unresolved externals
Target //:app_demo_cln failed to build
该函数存在于 CLN 库中,但名称修改似乎很奇怪......像 和 这样的东西,这样它们就不会匹配。cl_make_heap_string
cl_make_heap_string@cln@@YAPEAUcl_heap_string@1@PEBD@Z
cl_make_heap_string@@YAPEAUcl_heap_string@cln@@PEBD@Z
我试图改变编译器,改变编译器中的标志,并在互联网上寻找答案已经很多天了,但没有解决方案。
答:
感谢@Tsyvarev对链接错误性质的精确评论,这说明了命名的全局命名空间与特定命名空间存在问题。我确实检查了代码,这是一个片段:
定义(一些随机文件):.cc
namespace cln {
cl_heap_string* cl_make_heap_string (const char * s) { ... } }
}
声明(一些随机文件):.h
namespace cln {
struct heap_string {
// much more stuff here ...
friend cl_heap_string* cl_make_heap_string(const char* s);
};
}
所以有趣的是,直到 C++17 标准,两者之间的符号匹配,但使用 C++20 严格 () 行为,它们不再匹配(所以需要,但我想要一个真正的修复)。
我看了一下,也许 C++ 上的错误缺陷可能是这个“1477。在其命名空间之外的友元的定义“:https://cplusplus.github.io/CWG/issues/1477.html/permissive-
/permissive
原文:
在命名空间中首次声明的每个名称都是该命名空间的成员。如果非本地类中的友元声明首先声明一个类或函数95,则该友元类或函数是最内层封闭命名空间的成员。在该命名空间范围内提供匹配的声明(在授予友谊的类定义之前或之后),非限定查找 (6.5.3 [basic.lookup.unqual]) 或限定查找 (6.5.5 [basic.lookup.qual]) 不会找到友元的名称。
2012 年修复的文本:
在命名空间中首次声明的每个名称都是该命名空间的成员。如果非本地类中的友元声明首先声明一个类或函数95,则该友元类或函数是最内层封闭命名空间的成员。朋友声明本身不会使名称对非限定查找 (6.5.3 [basic.lookup.unqual]) 或限定查找 (6.5.5 [basic.lookup.qual]) 可见。[注意:如果在命名空间范围内提供匹配的声明(在授予友谊的类定义之前或之后),则友元的名称将在其命名空间中可见。如果调用好友函数...
所以,我更改了声明,它确实适用于 C++20 严格模式!
固定声明:
namespace cln {
struct heap_string {
// much more stuff here ...
friend cl_heap_string* cl_make_heap_string(const char* s);
};
// ADDED THIS PART HERE, TO ENFORCE THAT METHOD REALLY BELONGS TO cln:: NAMESPACE
cl_heap_string* cl_make_heap_string(const char* s);
}
所以,非常感谢您的帮助!
评论
/permissive
::
cl_make_heap_string
cln
cl_st_make1.cc
namespace cln { cl_heap_string* cl_make_heap_string (const char * s) { ... } }
string.h
namespace cln { struct heap_string { friend cl_heap_string* cl_make_heap_string(const char* s); }; }