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

寻求优化C++中std::apply可变参数带索引传递的indexed()实现方案

寻求优化C++中std::apply可变参数带索引传递的indexed()实现方案

我最近在琢磨怎么用std::apply的时候,能干净利落地给可变参数带上索引——就是把元组拆包传给可调用对象时,每个参数都能拿到自己的位置索引。

先给大家看一个不算完美但还算简洁的写法:

const auto animals = std::make_tuple("cow", "dog", "sheep");

std::apply([](const auto& ... str){
    const auto print = [](const auto& str, size_t index){
        std::cout << index << ": " << str << '\n';
    };

    size_t i = 0;
    (print(str, i++), ...);
}, animals);

这个写法比用std::index_sequence加重载的方式清爽不少,因为不用在lambda外面写额外代码——毕竟C++的块作用域里不能定义模板,要是用index_sequence的话还得在外头搞辅助类。但它的问题也很明显:得手动维护一个可变状态i,不够优雅,我觉得索引应该是能隐式获取、不用用户手动管理的才对。

我心里理想的用法是类似JavaScript的解构(可惜C++不允许把结构化绑定当函数参数),大概是这样:

// 理想状态(C++目前没法直接实现)
std::apply([](auto ... {value, index}){ 
    // 直接用每个参数的index和value
}, animals);

退而求其次,我自己实现了一个indexed()包装器,用法是这样的:

const auto animals = std::make_tuple("cow", "dog", "sheep");

std::apply(indexed([](auto ... indexedValue){
    const auto print = [](const auto& indexedValue){
        const auto &[index, value] = indexedValue;
        std::cout << index << ": " << value << '\n';
    };

    (print(indexedValue), ...);
}), animals);

这个API比手动计数器或者写一堆模板 boilerplate 要好,只需要把可调用对象包进indexed()里就行,也不用修改STL的内容。但问题是我写的indexed()实现不够高效,生成的汇编指令比第一个手动计数器的版本多不少。

下面是我目前的indexed()实现,希望能得到优化建议:

#include <cstddef>
#include <type_traits>
#include <tuple>

namespace detail {

template <size_t I, typename T>
struct _indexed
{
    constexpr static size_t index = I;
    T value;
    constexpr _indexed(std::integral_constant<size_t, I>, T t)
        : value(t)
    {}

    template <size_t Elem>
    friend constexpr auto get(const detail::_indexed<I, T>& v) noexcept ->
    std::tuple_element_t<Elem, detail::_indexed<I, T>>{
        if constexpr (Elem == 0)
            return I;
        if constexpr (Elem == 1)
            return v.value;
    }
};

template <size_t I, typename T>
_indexed(std::integral_constant<size_t, I>, T) -> _indexed<I, T>;

template <typename CRTP>
class _add_indices
{
public:
    template <typename ... Args>
    constexpr decltype(auto) operator()(Args &&... args) const noexcept {
        return (*this)(std::make_index_sequence<sizeof...(Args)>(), std::forward<Args>(args)...);
    }

private:
    template <typename ... Args, size_t ... I>
    constexpr decltype(auto) operator()(std::index_sequence<I...>, Args ... args) const noexcept {
        return static_cast<const CRTP&>(*this).callable(_indexed(std::integral_constant<size_t, I>{}, std::forward<Args>(args))...);
    }
};

}

template <size_t I, typename T>
struct std::tuple_size<detail::_indexed<I, T>> : std::integral_constant<size_t, 2> {};

template <size_t I, typename T>
struct std::tuple_element<0, detail::_indexed<I, T>>
{
    using type = size_t;
};

template <size_t I, typename T>
struct std::tuple_element<1, detail::_indexed<I, T>>
{
    using type = T;
};

template <typename Callable>
constexpr auto indexed(Callable c) noexcept{
    struct _c : detail::_add_indices<_c> {
        Callable callable;
    };

    return _c{.callable = c};
}

这个实现的核心思路是给每个参数包装一个带索引的_indexed结构体,然后特化std::tuple_size和std::tuple_element让它支持结构化绑定。但编译出来的效率不如手动计数器的版本,想问问有没有办法优化这个实现,让它更高效,同时保持API的简洁性?

备注:内容来源于stack exchange,提问作者Sergey Kolesnik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 08:47:48