自定义类型特化tuple相关接口后仍无法使用std::apply怎么办?
问题原因与解决方案
核心问题本质
这是不符合C++标准实现规则导致的非预期错误,std::apply本身完全支持自定义类型适配,你遇到的问题源于两个实现错误:
- 违规向
std命名空间新增内容:C++标准明确规定,除针对std已有的类模板对用户自定义类型做全特化外,向std命名空间新增任何函数、类、模板的声明/定义都属于未定义行为。你编写的get系列函数是新增的函数模板重载,不属于合法的特化操作。 - 不符合
std::apply的get函数查找规则:std::apply对get函数的查找遵循实参依赖查找(ADL)优先的规则,会先在传入参数的类型所在的命名空间查找get,找不到才会查找std命名空间下的get。你的自定义类型Foo定义在全局命名空间,而你把get放到了std命名空间,ADL阶段无法找到对应重载,最终匹配失败。
可行解决方案
你已经合法完成了std::tuple_size和std::tuple_element的全特化(这两个属于std已有的类模板特化,符合标准要求),只需要把所有get函数模板从std命名空间移出,放到和Foo同层级的命名空间即可,修改后的代码示例如下:
#include <tuple> struct Foo { public: explicit Foo(int a) : a{ a } {} auto &get_a() const { return a; } auto &get_a() { return a; } private: int a; }; // 特化std的tuple相关类模板是合法操作 namespace std { template<> struct tuple_size<Foo> { constexpr static auto value = 1; }; template<> struct tuple_element<0, Foo> { using type = int; }; } // namespace std // get放到和Foo同命名空间(此处为全局命名空间),供ADL查找 template<size_t I> constexpr auto get(Foo &t) -> int & { return t.get_a(); } template<size_t I> constexpr auto get(const Foo &t) -> const int & { return t.get_a(); } template<size_t I> constexpr auto get(Foo &&t) -> int && { return std::move(t.get_a()); } template<size_t I> constexpr auto get(const Foo &&t) -> const int && { return std::move(t.get_a()); } // 以下调用可以正常编译运行 auto foo = Foo{ 1 }; auto f = [](int) { return 2; }; auto result = std::apply(f, foo);
修改完成后三大编译器均可正常编译通过,符合std::apply的使用要求。
内容的提问来源于stack exchange,提问作者Erik Valkering
相关产品推荐
相关产品推荐

