You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为std::apply特化std::tuple_size与std::get时遭遇编译错误

为结构体特化std::tuple_size/std::get后,std::apply调用失败的问题

我尝试为一个简单结构体特化std::tuple_size和std::get,想配合std::apply在泛型函数中使用,但调用std::apply时编译报错。有意思的是,把std::apply和std::__apply_impl的实现复制到本地命名空间后调用却能正常运行。请问我哪里操作错了?

最小可复现代码

#include <iostream>
#include <array>
#include <tuple>
#include <functional>

// 定义一个包含不同类型成员的简单结构体A
struct A {
    int i;
    double d;
};

// 为A特化std::tuple_size和std::get
namespace std {
template <> struct tuple_size<A> : std::integral_constant<std::size_t, 2> { };  // 注释里的"3 members in A"是笔误,实际是2个成员

template <std::size_t N>  constexpr decltype(auto) get (const A& a) { 
    if constexpr (N == 0) return (a.i); 
    else if constexpr (N == 1) return (a.d); 
}
}

// 复制std::__apply_impl和std::apply的实现到本地命名空间My
namespace My {
    template <class Callable, class Tuple, size_t... _Indices>
    constexpr decltype(auto) apply_impl(Callable&& _Obj, Tuple&& _Tpl,std::index_sequence<_Indices...>)
    {
        using namespace std;
        return std::invoke (forward<Callable>(_Obj), get<_Indices>(forward<Tuple>(_Tpl))...);
    }
    
    template <class Callable, class Tuple>
    constexpr decltype(auto) apply(Callable&& Obj, Tuple&& Tpl)  
    {
        using namespace std;
        return apply_impl(forward<Callable>(Obj), forward<Tuple>(Tpl), make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});
    }
}

int main () 
{
    A a {0, 1.5};
// #define COMPILER_ERROR // 打开这个宏会触发编译错误
#ifdef COMPILER_ERROR 
    std::apply ([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, a);
#else
    My::apply ([](auto&&... args) {((std::cout << args << ' '), ...); std::cout << "\n";}, a);
#endif

    return 0;
}

问题原因

你的代码存在两个核心问题:

  1. 只特化了const版本的std::get:你仅提供了const A&参数的std::get特化,但std::apply处理非const左值(比如main里的a)时,标准库实现会尝试匹配非const版本的get,而你没有提供这个重载,导致编译失败。
  2. 本地My::apply能运行的原因:你自己实现的My::apply中,get<_Indices>(forward<Tuple>(Tpl))的参数是A&,而const版本的get可以绑定到非const左值,因此能成功匹配;但标准库的std::apply实现可能对重载匹配的逻辑更严格,或者会优先尝试非const版本的get,导致找不到合适的重载。

解决方法

补充完整std::get的特化版本,覆盖所有cv限定和值类别:

namespace std {
template <> struct tuple_size<A> : std::integral_constant<std::size_t, 2> { };

// 非const左值引用版本
template <std::size_t N> constexpr decltype(auto) get(A& a) {
    if constexpr (N == 0) return (a.i);
    else if constexpr (N == 1) return (a.d);
}

// const左值引用版本(你原来的代码)
template <std::size_t N> constexpr decltype(auto) get(const A& a) {
    if constexpr (N == 0) return (a.i);
    else if constexpr (N == 1) return (a.d);
}

// 右值引用版本
template <std::size_t N> constexpr decltype(auto) get(A&& a) {
    if constexpr (N == 0) return (std::move(a.i));
    else if constexpr (N == 1) return (std::move(a.d));
}

// const右值引用版本(可选,覆盖所有情况)
template <std::size_t N> constexpr decltype(auto) get(const A&& a) {
    if constexpr (N == 0) return (std::move(a.i));
    else if constexpr (N == 1) return (std::move(a.d));
}
}

补充完这些特化后,std::apply就能正常调用了。另外,建议修正代码里的注释笔误(把"3 members in A"改成"2 members in A"),避免混淆。

内容的提问来源于stack exchange,提问作者Catriel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:37:47