将 std::tuple 用于模板参数列表而不是类型列表

我正在尝试调用这样的模板函数:

I'm trying to make a call to a templated function like this :

typedef std::tuple<int, double, bool> InstrumentTuple;

Cache cache;
InstrumentTuple tuple = cache.get<InstrumentTuple>();

我知道我可以简单地"传递元组的类型.这是我所知道的,但这很麻烦,因为我对这个函数进行了很多调用,而且元组很长:

I know I could "simply" pass the types of the tuple. This is what I do know but it is quite cumbersome since I make a lot of calls to this function and since the tuples are quite long:

InstrumentTuple tuple = c.get<int, double, bool>(); // syntax I'd like to avoid

所以我尝试了 get 方法的多种实现,但没有成功:

So I tried multiple implementations of the get method, but with no success :

#include <tuple>

class Cache
{
private:
    template<int I, typename T, typename = typename std::enable_if<I == std::tuple_size<T>::value>::type>
    std::tuple<> get() // line 6
    {
        return std::tuple<>();
    }

    template<int I, typename T, typename = typename std::enable_if<I != std::tuple_size<T>::value>::type>
    std::tuple<typename std::tuple_element<I,T>::type, decltype(get<I+1, T>())> get() // line 12
    {
        std::tuple<typename std::tuple_element<I,T>::type> value;
        return std::tuple_cat(value, get<I+1, T>());
    }

public:
    template<typename T>
    T get()
    {
        return get<0, T>(); // line 22
    }
};

int main(int argc, char** argv)
{
    Cache cache;
    typedef std::tuple<int, double, bool> InstrumentTuple;
    InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30
}

这给了我这个错误:

main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]':
main.cpp:30:56:   required from here
main.cpp:22:26: error: no matching function for call to 'Cache::get()'
main.cpp:22:26: note: candidates are:
main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get()
main.cpp:6:18: note:   template argument deduction/substitution failed:
main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>'
main.cpp:12:81: note: template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get()
// ----- Important part
main.cpp:12:81: note:   template argument deduction/substitution failed:
main.cpp: In substitution of 'template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() [with int I = 0; T = std::tuple<int, double, bool>; <template-parameter-1-3> = <missing>]':
// -----
main.cpp:22:26:   required from 'T Cache::get() [with T = std::tuple<int, double, bool>]'
main.cpp:30:56:   required from here
main.cpp:12:81: error: no matching function for call to 'Cache::get()'
main.cpp:12:81: note: candidate is:
main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get()
main.cpp:6:18: note:   template argument deduction/substitution failed:
main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>'
main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]':
main.cpp:30:56:   required from here
main.cpp:20:7: note: template<class T> T Cache::get()
main.cpp:20:7: note:   template argument deduction/substitution failed:
main.cpp:22:26: error: wrong number of template arguments (2, should be 1)

我不明白为什么缺少模板参数.

I don't get why is there a missing template parameter.

所以我尝试了另一种实现:

So I tried another implementation :

#include <tuple>

class Cache
{
private:
    template<int>
    std::tuple<> get() // line 7
    {
        return std::tuple<>();
    }

    template<int index, typename type, typename... rest>
    std::tuple<type, rest...> get() // line 13
    {
        return std::tuple_cat(std::tuple<type>(), get<index+1, rest...>());
    }

public:
    template<template<typename... types> class tuple>
    typename std::tuple<(tuple::types)...> get()
    {
        return get<0, (tuple::types)...>();
    }
};  // line 24

int main(int argc, char** argv)
{
    Cache cache;
    typedef std::tuple<int, double, bool> InstrumentTuple;
    InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30
}

然后我得到这个错误:

// ----- Important part
main.cpp:24:1: error: expected identifier before '}' token
main.cpp:24:1: error: expected unqualified-id before '}' token
// -----
main.cpp: In function 'int main(int, char**)':
main.cpp:30:56: error: no matching function for call to 'Cache::get()'
main.cpp:30:56: note: candidates are:
main.cpp:7:18: note: template<int <anonymous> > std::tuple<> Cache::get()
main.cpp:7:18: note:   template argument deduction/substitution failed:
main.cpp:13:31: note: template<int index, class type, class ... rest> std::tuple<_Head, _Tail ...> Cache::get()
main.cpp:13:31: note:   template argument deduction/substitution failed:

同样,由于缺少标识符,我不明白错误.

Again, I don't understand the errors due to a missing identifier.

我现在想知道我想要实现的目标是否可能.是否可以像我想要的那样使用 std::tuple ?还是有更好的办法?

I'm wondering now if what I want to achieve is even possible. Is it possible to use a std::tuple like I want to ? Or is there a better way ?

推荐答案

您的第一个解决方案失败了,因为 get 的第二个重载在其自己的返回类型声明处不可见;要解决这个问题,您需要将返回类型计算分离到它自己的子程序中.

Your first solution is failing because the second overload to get is not visible at the point of its own return type declaration; to get around this you would need to separate out the return type computation into its own subprogram.

第二种方案更接近;问题是你只是在推断 template std::tuple,而不是它的参数.推断可变参数(例如,tuple 的类型参数)的一种简单方法是通过空标记结构,需要一个额外的间接级别:

The second solution is closer; the problem is that you're only inferring the template std::tuple, not its arguments. An easy way to infer variadic arguments (e.g. type arguments to tuple) is through an empty tag structure, requiring one extra level of indirection:

template<typename T> struct type_tag {};

class Cache {
    // ... (as above)

    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return get<0, Ts...>();
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};

您应该检查是否可以使用包扩展而不是递归来编写解决方案,例如:

You should check to see whether you can write the solution using pack expansion instead of recursion, for example:

template<typename T> struct type_tag {};

class Cache {
    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return std::tuple<Ts...>{Ts{}...};
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};

相关文章