返回取决于sizeof的变量类型..参数包

Return variable type depending on sizeof... parameter pack

本文关键字:类型 参数 变量 取决于 sizeof 返回      更新时间:2023-10-16

我希望创建一个函数,如果传递了多个模板参数,则返回一个装箱的元组,如果只传递了一个模板参数则返回未装箱的值。

例如,我希望foo<int>()返回intfoo<int, float>返回类型为std::tuple<int, float>的某物。

我为达到这个效果所做的一切努力都失败了。

考虑使用类型特征结构的以下方法:

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中是不可能的,因为确定返回类型所需的所有信息在编译时都是可用的。然而,我正在努力寻找什么工具可以让我做到这一点。如有任何帮助和建议,我们将不胜感激。

我通常使用一个结构进行专门化:

#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>();
}

啊,我终于休息了一下,喝了一杯(这也是经过几个小时试图找到解决方案的结果!),终于找到了答案。

答案是对最后一种方法的修改:

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 */ }

通过使用两个伪参数,编译器可以明确地确定要调用哪个函数。

我会使用标记调度。

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)...);
}

然后我会添加完美的转发。

我发现对过载的推理比对专业化的推理更容易。