C++20 LNK2019 MSVC 错误,而 C++17 有效

C++20 LNK2019 error with MSVC, while C++17 works

提问人:igormcoelho 提问时间:10/1/2023 更新时间:10/3/2023 访问量:104

问:

我在使用 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_stringcl_make_heap_string@cln@@YAPEAUcl_heap_string@1@PEBD@Zcl_make_heap_string@@YAPEAUcl_heap_string@cln@@PEBD@Z

我试图改变编译器,改变编译器中的标志,并在互联网上寻找答案已经很多天了,但没有解决方案。

visual-c++ cmake c++20 bazel lnk2019

评论

1赞 igormcoelho 10/1/2023
似乎将标志与 c++20 for windows 一起使用可以构建项目。然而,我不明白编译器本身生成的对象如何导致链接失败。/permissive
2赞 Tsyvarev 10/1/2023
根据错误消息,遗漏的符号来自全局命名空间(之前没有部分),但现有符号来自命名空间。可能 cln 项目在函数的声明和定义方面有问题。::cl_make_heap_stringcln
0赞 igormcoelho 10/1/2023
感谢您@Tsyvarev回复,这真的很奇怪。CLN项目是正确的,函数确实在CLN命名空间下,并且在C++17 MSVC,GCC和CLANG,以及C++20 GCC和CLANG上工作正常。只有带有 /permissive 的 C++20 msvc - 它会中断。很奇怪
0赞 igormcoelho 10/1/2023
确切的情况是: 文件上的定义: 文件上的声明: 确实,至少对我来说,如果一个朋友进入全局或命名空间下,这是相当令人困惑的,也许编译器也混淆了 xDcl_st_make1.ccnamespace cln { cl_heap_string* cl_make_heap_string (const char * s) { ... } }string.hnamespace cln { struct heap_string { friend cl_heap_string* cl_make_heap_string(const char* s); }; }

答:

0赞 igormcoelho 10/3/2023 #1

感谢@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); 
}

所以,非常感谢您的帮助!