Clang 16处理带模板友元函数的niebloid兼容方案问询
Clang 16对模板类模板隐藏友元Niebloid的兼容问题
我实现了一套基于niebloid模式的代码,除Clang 16外,Clang 11及更早、Clang 17及更高版本均可正常编译,但Clang 16会报重定义和函数匹配失败错误。
原代码
#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__) namespace mylib { namespace detail { auto func(struct poison&) = delete; struct func_function { constexpr auto operator()(auto&& a) const -> decltype(func(FWD(a))) { return func(FWD(a)); } }; } inline namespace niebloid { inline constexpr auto func = detail::func_function{}; } template<typename T> struct some_type { template<typename U> friend constexpr auto func(U&&) -> int { return 42; } }; } int main() { return mylib::func(mylib::some_type<int>{}); }
Clang 16编译错误
<source>:21:31: error: redefinition of 'func' as different kind of symbol friend constexpr auto func(U&&) -> int { ^ <source>:29:24: note: in instantiation of template class 'mylib::some_type<int>' requested here return mylib::func(mylib::some_type<int>{}); ^ <source>:15:31: note: previous definition is here inline constexpr auto func = detail::func_function{}; ^ <source>:29:12: error: no matching function for call to object of type 'const detail::func_function' return mylib::func(mylib::some_type<int>{}); ^~~~~~~~~~~ <source>:8:28: note: candidate template ignored: substitution failure [with a:auto = mylib::some_type<int>]: no matching function for call to 'func' constexpr auto operator()(auto&& a) const -> decltype(func(FWD(a))) { ^ ~~~~ 2 errors generated. Compiler returned: 1
现有不可行方案
将some_type改为非模板类或把隐藏友元改为非模板函数可规避问题,但这两种方案均不适用于我的代码库。
改动最小的兼容方案
核心思路是通过调整内部调用的函数名,避免Clang 16误判名字冲突,同时保留原有对外接口和ADL查找逻辑:
修改后代码
#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__) namespace mylib { namespace detail { auto func_impl(struct poison&) = delete; struct func_function { constexpr auto operator()(auto&& a) const -> decltype(func_impl(FWD(a))) { return func_impl(FWD(a)); } }; } inline namespace niebloid { inline constexpr auto func = detail::func_function{}; } template<typename T> struct some_type { template<typename U> friend constexpr auto func_impl(U&&) -> int { return 42; } }; } int main() { return mylib::func(mylib::some_type<int>{}); }
原理说明
Clang 16存在编译器bug,会错误将模板类的模板友元函数名与同一命名空间下的niebloid对象名判定为冲突。通过将内部实际调用的函数名改为func_impl,既保留了对外的func接口不变,又规避了名字冲突,同时ADL机制仍能正常找到some_type的友元函数func_impl,完全保留原有逻辑。
兼容性说明
该问题仅存在于Clang 16版本,Clang 17及更高版本已修复此bug,Clang 11及更早版本无此问题。若无法升级编译器,上述改名方案是改动最小的兼容方式。
内容的提问来源于stack exchange,提问作者Guillaume Racicot
相关产品推荐
相关产品推荐

