g++编译跨编译单元lambda代码报错,是否符合C++标准?
你的代码合法性与跨编译单元问题分析
首先直接给结论:你的代码在C++14及以后的标准中是合法的,GCC 7.3.0的报错属于编译器实现bug,而Clang和ICC的行为是符合标准的。同时,从标准角度来说,跨编译单元声明并定义wrapped_foo是允许的,但需要注意编译器版本的兼容性问题。
为什么GCC 7.3.0会报错?
GCC 7.x系列在处理lambda类型的跨编译单元decltype声明时存在缺陷:lambda的类型是匿名且唯一的,但标准允许用decltype(wrap(foo))来指代这个匿名类型,并且跨编译单元共享这个类型的声明。不过GCC 7.3.0在不同编译单元中会错误地认为这个decltype指代的类型不匹配,从而抛出错误。添加-fpermissive会让GCC放宽类型检查,绕过这个bug,但这只是临时的 workaround。
而Clang和ICC对标准的实现更准确,能够正确识别跨编译单元中decltype(wrap(foo))指代的是同一个lambda类型,所以不会报错。
能不能跨编译单元声明并定义wrapped_foo?
从C++标准层面来说是完全可以的,但有几个注意事项:
- 确保所有参与编译的单元都启用了C14或更高版本的标准(因为
auto作为函数返回类型是C14引入的特性)。 - 如果你必须使用GCC 7.3.0,除了用
-fpermissive之外,更稳妥的方式是避免依赖lambda的匿名类型,改用明确命名的类型来封装,比如:
方案1:使用std::function
#include <functional> std::function<int(int)> wrap(int (*f)(int)) { return [f](int i) { return f(i); }; } int foo(int); extern std::function<int(int)> wrapped_foo; int func() { return wrapped_foo(2); }
方案2:自定义 functor 类型
struct FunctionWrapper { int (*target)(int); int operator()(int i) const { return target(i); } }; FunctionWrapper wrap(int (*f)(int)) { return {f}; } int foo(int); extern FunctionWrapper wrapped_foo; int func() { return wrapped_foo(2); }
这两种方式的类型都是明确命名的,在所有主流编译器(包括GCC 7.3.0)中都能正常跨编译单元使用,不会出现类型不匹配的问题。
另外,如果你能升级GCC到8.x及以上版本,这个bug已经被修复,不需要任何额外参数就能正常编译你的原始代码。
内容的提问来源于stack exchange,提问作者jks
相关产品推荐
相关产品推荐

