当我不是常量表达式时,要处理std :: tuple的ith成员?

And*_*zos 3 c++ c++17

假设我有以下功能模板:

template<typename T>
void mutate(T& t) { /*...*/ }
Run Code Online (Sandbox Code Playgroud)

并且我有以下课程模板:

template<typename... Args>
class Processor {
    std::tuple<Args...> tup;

    // call mutate on the ith tuple element
    void process(size_t i) {
        mutate(std::get<i>(tup)); // ERROR
    }
}
Run Code Online (Sandbox Code Playgroud)

这是行不通的,因为的i参数process不是常量表达式。(它不能是一个常量表达式,因为i直到运行时才知道)

在不修改process或的签名的情况下mutuate,是否可以有效地完成这项工作?即我如何更改实现process以使其调用mutateith成员tup

Bar*_*rry 6

您需要获取运行时索引并将其提升为常量表达式。最简单的方法是只使用Boost.Mp11,它带有一个为此功能:

template<typename... Args>
class Processor {
    std::tuple<Args...> tup;

    // call mutate on the ith tuple element
    void process(size_t i) {
        mp_with_index<sizeof...(Args)>(i, [&](auto I){
            mutate(std::get<I>(tup));
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

要做的mp_with_index是对最大大小(sizeof...(Args))和运行时大小(i)进行常量表达式,然后使用整数常量(即运行时大小提升为常量表达式)来调用可调用对象(lambda)。


您可以自己std::index_sequence构建一个函数指针数组,然后调用正确的指针来实现:

template <size_t... Is, typename F>
decltype(auto) mp_with_index(size_t i, F f, std::index_sequence<Is...>) {
    using R = decltype(f(std::integral_constant<size_t, 0>{}));
    using P = R(*)(F&);
    static constexpr P fns[] = {
        +[](F& f) -> R { return f(std::integral_constant<size_t, Is>{}); }...
    };
    return fns[i](f);
}

template <size_t N, typename F>
decltype(auto) mp_with_index(size_t i, F f) {
    return mp_with_index(i, f, std::make_index_sequence<N>());
}
Run Code Online (Sandbox Code Playgroud)

(请注意,Boost.Mp11实现比这更好,这在功能上是正确的)。


Mar*_*ork 5

借助一些辅助功能,我们可以做到。

#include <tuple>
#include <iostream>

template<typename T>
void mutate(T& t)
{
    std::cout << typeid(t).name() << "\n";
}

template<typename T, std::size_t... Seq>
void processes_each(int i, T& t, std::index_sequence<Seq...> const&)
{
    bool discard[] = {  false,
                        (i == Seq ? (mutate(std::get<Seq>(t)), true) : false) ...
                     };
    (void)discard;
}

template<typename... Args>
class Processes
{
    std::tuple<Args...> tup;

    public:
    void process(size_t i) {
        processes_each(i, tup, std::make_index_sequence<sizeof...(Args)>());
    }
};

int main()
{
    Processes<int, double>      p1;
    p1.process(0);
}
Run Code Online (Sandbox Code Playgroud)

  • 真好 但是问题被标记为C ++ 17,因此您可以使用模板折叠并稍微简化一下(避免“舍弃”)“ process_each()”的主体。例如(((i == Seq?(mutate(std :: get &lt;Seq&gt;(t)),0):0),...); ` (2认同)