我正在尝试调用这样的模板化函数:
typedef std::tuple<int, double, bool> InstrumentTuple;
Cache cache;
InstrumentTuple tuple = cache.get<InstrumentTuple>();
我知道我可以“简单地”传递元组的类型。这就是我所知道的但是它非常麻烦,因为我对这个函数进行了很多调用,因为元组很长:
InstrumentTuple tuple = c.get<int, double, bool>(); // syntax I'd like to avoid
所以我尝试了get方法的多个实现,但没有成功:
#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)
我不知道为什么缺少模板参数。
所以我尝试了另一种实现:
#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:
同样,我不理解由于缺少标识符而导致的错误。
我现在想知道我想要达到的目标是否可行。可以像我想要的那样使用std::tuple
吗?或者有更好的方法吗?
答案 0 :(得分:6)
你的第一个解决方案失败了,因为get
的第二个重载在它自己的返回类型声明中是不可见的;要解决这个问题,你需要将返回类型计算分离到它自己的子程序中。
第二种解决方案更接近;问题是你只是推断模板 std::tuple
,而不是它的参数。推断可变参数的简单方法(例如tuple
的类型参数)是通过空标记结构,需要一个额外的间接级别:
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>{});
}
};
您应该检查是否可以使用包扩展而不是递归来编写解决方案,例如:
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>{});
}
};
答案 1 :(得分:3)
以下是我提出的建议:
#include <tuple>
struct Cache;
/* typename = std::tuple<...> */
template<int, typename> struct cache_getter;
/* typename = parameters from std::tuple<...> */
template<int, typename...> struct tuple_walker;
template<int I, typename... Ts> struct cache_getter<I, std::tuple<Ts...> > {
static std::tuple<Ts...> get(Cache & c);
};
struct Cache {
protected:
template<int, typename...> friend struct tuple_walker;
private:
/* here T is a type from within a std::tuple<...> */
template<int I, typename T> std::tuple<T> get_ex() {
return std::tuple<T>();
}
public:
/* here T is actually a std::tuple<...> */
template<typename T> T get() {
return cache_getter<0, T>::get(*this);
}
};
/* since std::tuple_cat only accepts 2 std::tuples per call but we don't have control over the number of types in the passed in std::tuple, we'll need to chain our calls */
template<typename...> struct my_tuple_cat;
template<typename H, typename... T> struct my_tuple_cat<H, T...> {
static auto cat(H h, T... t) -> decltype(std::tuple_cat(h, my_tuple_cat<T...>::cat(t...)))
{ return std::tuple_cat(h, my_tuple_cat<T...>::cat(t...)); }
};
template<typename T> struct my_tuple_cat<T> {
static T cat(T t) { return t; }
};
/* this one is used to call Cache.get_ex<int I, typename T>() with incrementing values for I */
template<int I, typename H, typename... T> struct tuple_walker<I, H, T...> {
static std::tuple<H, T...> get(Cache & c) {
return my_tuple_cat<std::tuple<H>, std::tuple<T...>>::cat(c.get_ex<I, H>(), tuple_walker<I + 1, T...>::get(c));
}
};
template<int I, typename H> struct tuple_walker<I, H> {
static std::tuple<H> get(Cache & c) {
return c.get_ex<I, H>();
}
};
/* this one will forward the types in std::tuple<...> to tuple_walker to get each tuple separately */
template<int I, typename... Ts> std::tuple<Ts...> cache_getter<I, std::tuple<Ts...> >::get(Cache & c) {
return tuple_walker<I, Ts...>::get(c);
}
int main(int argc, char ** argv) {
Cache cache;
typedef std::tuple<int, double, bool> InstrumentTuple;
InstrumentTuple tuple = cache.get<InstrumentTuple>();
return 0;
}
我希望这是值得的。我还没有在C ++ 11中做过多少工作,所以这可能不是最佳解决方案。
可以找到它编译的证明here