我希望创建一个函数,如果传递了多个模板参数,则返回一个盒装元组;如果只传递一个模板参数,则返回一个未装箱的值。
例如,我希望foo<int>()
返回int
和foo<int, float>
以返回类型为std::tuple<int, float>
的内容。
我实现这种效果的所有尝试都失败了。
使用 typetrait struct 考虑以下方法:
template<typename... T>
struct return_type {
typedef std::tuple<T...> type;
};
template<>
struct return_type<int> {
typedef int type;
};
// ... insert partial specializations for other supported primitive types
template<typename... T>
auto foo() -> typename return_type<T...>::type {
if (sizeof...(T) == 1)
return zap<T...>(); // Returns something of type T, where T is the first parameter
else
return bar<T...>(); // Assume this returns a std::tuple<T...>
}
由于foo
正文中的返回类型不同,这将无法编译。
或者,这是尝试使用decltype
:
<template T>
T singular();
<template... T>
std::tuple<T...> multiple();
template <typename... T>
auto foo() -> decltype(sizeof...(T) == 1 ? singular() : multiple())
{
... // as above
}
这也将无法编译,因为三元运算符期望两个分支返回相同的类型。
最后,使用简单递归解包的天真方法也失败了:
template<typename T>
T foo() { return T{}; // return something of type T }
template<typename... T>
std::tuple<T...> foo() { return bar<T...>(); // returns a tuple }
这当然失败了,因为编译器无法确定要调用哪个重载函数。
我无法理解为什么在C ++ 11中无法实现这样的功能,因为在编译时可以获得确定返回类型所需的所有信息。然而,我很难看到哪些工具可以让我这样做。任何帮助和建议将不胜感激。
答案 0 :(得分:4)
我通常使用结构进行专业化:
#include <iostream>
#include <tuple>
namespace Detail {
template <typename...Ts>
struct Foo {
typedef std::tuple<Ts...> return_type;
static return_type apply() { return return_type(); }
};
template <typename T>
struct Foo<T> {
typedef T return_type;
static return_type apply() { return return_type(); }
};
}
template <typename...Ts>
typename Detail::Foo<Ts...>::return_type foo() {
return Detail::Foo<Ts...>::apply();
}
int main ()
{
std::tuple<int, int> t = foo<int, int>();
int i = foo<int>();
}
答案 1 :(得分:3)
呃,我终于休息一下并拿起一杯饮料(这是在经过几个小时试图找到解决方案之后)才得出答案。)
答案是对最后一种方法的修改:
template<typename T>
T foo() { return zap<T>(); /* returns a T */ }
template<typename T1, typename T2, typename... Ts>
std::tuple<T1, T2, Ts...> foo() { return bar<T1, T2, Ts...>(); /* returns a tuple */ }
通过使用两个伪参数,编译器可以明确地解析要调用的函数。
答案 2 :(得分:1)
我会使用标签调度。
template<class...Ts> struct many :std::true_type {};
template<class T>struct many :std::false_type {};
template<class...Ts> struct return_value {
typedef std::tuple< typename std::decay<Ts>::type... > type;
};
template<class T>struct return_value : std::decay<T> {};
template<typename T>
T singular( T&& t ) {
return std::forward<T>(t);
}
template<typename... Ts>
typename return_value<Ts...>::type multiple( Ts&&... ts ) {
return { std::forward<Ts>(ts)... };
}
template<typename...T>
typename return_value<T...>::type worker(std::false_type, T&&...t ) {
static_assert( sizeof...(T)==1, "this override is only valid with one element in the parameter pack" );
return singular(std::forward<T>(t)...);
}
template<typename...Ts>
typename return_value<Ts...>::type worker(std::true_type, Ts&&...t) {
return multiple(std::forward<Ts>(t)...);
}
template<class...Ts>
auto foo(Ts&&... t)
-> decltype(worker( many<Ts...>(), std::declval<Ts>()...) )
{
return worker(many<Ts...>(), std::forward<Ts>(t)...);
}
然后我会添加完美转发。
我发现有关重载的理由比关于特殊化更容易。