GCC <=10下兼容模板ADL特性的无命名空间感知实现方案咨询
问题根源
原代码依赖C20新增的ADL规则:未限定名称的函数调用如果显式指定模板参数,也可以触发ADL查找。C20之前的标准不支持该规则,因此get<N>(args)的写法无法通过ADL找到foo、bar命名空间下的对应实现,导致编译失败。
兼容方案(支持GCC 8及以上、C++17及更低版本)
核心思路是将模板参数N转换为函数参数传递,规避显式模板参数导致的ADL失效问题,完全不需要修改for_each的逻辑适配特定命名空间。
实现步骤
- 全局定义轻量标签类型,用于传递下标N:
template<std::size_t N> struct size_tag {};
- 在foo、bar命名空间下新增一行转发重载,将带标签的get调用转发到原有实现(侵入性极低,不影响原有代码逻辑):
// foo命名空间新增内容 template<std::size_t N, typename... Args> auto get(tuple<Args...> t, size_tag<N>) { return get<N>(t); } // bar命名空间同理新增内容 template<std::size_t N, typename... Args> auto get(tuple<Args...> t, size_tag<N>) { return get<N>(t); }
- 修改
for_each内的get调用方式,改用标签参数触发ADL:
template<class Tuple, std::size_t... N> auto for_each(Tuple& args, std::index_sequence<N...>) { (get(args, size_tag<N>{}), ...); }
完整可运行代码
#include <utility> template<std::size_t N> struct size_tag {}; namespace foo { template<typename... Args> struct tuple : Args... { }; template<std::size_t N, typename... Args> auto get(tuple<Args...>) { return 0; } template<std::size_t N, typename... Args> auto get(tuple<Args...> t, size_tag<N>) { return get<N>(t); } } namespace bar { template<typename... Args> struct tuple : Args... { }; template<std::size_t N, typename... Args> auto get(tuple<Args...>) { return 0; } template<std::size_t N, typename... Args> auto get(tuple<Args...> t, size_tag<N>) { return get<N>(t); } } template<class Tuple, std::size_t... N> auto for_each(Tuple& args, std::index_sequence<N...>) { (get(args, size_tag<N>{}), ...); } int main() { struct test { }; foo::tuple<test> t; for_each(t, std::make_index_sequence<1>()); return 0; }
无侵入式变种(不修改foo、bar命名空间代码)
如果完全不能修改原有foo、bar的代码,可以自定义一个调度函数,通过对tuple类型的匹配手动转发到对应命名空间的get实现,不过该方案扩展性弱于标签法,新增自定义tuple命名空间时需要更新调度逻辑。
内容的提问来源于stack exchange,提问作者Jean-Michaël Celerier
相关产品推荐
相关产品推荐

