我们如何实现一个可变参数模板,给定类型T
和类型列表E
1 ,E
2 ,... E
N ,根据重载决议,确定从T
到该类型的转换的列表类型最好?< / p>
如果没有最佳转换,
void
应该是输出 - 换句话说,当存在歧义或T
无法转换为列表中的任何类型时。
请注意,这意味着我们的模板应该是SFINAE友好的,即当没有最佳转换时不会出现硬错误。
以下static_assert
应该会成功:
static_assert( std::is_same< best<int, long, short>, void >{}, "" );
static_assert( std::is_same< best<int, long, std::string>, long >{}, "" );
static_assert( std::is_same< best<int>, void >{}, "" );
(假设,为简单起见,best
是引用实际模板的别名模板)
此案例未指明:
static_assert( std::is_same< best<int, int, int>, ???>{}, "" );
此处应接受void
或int
。 (如果选择后者,那么我们仍然可以在包装器模板中检查结果类型是否在列表中包含两次,如果是,则输出void
代替。)
答案 0 :(得分:6)
我目前最好的方法:
#include <type_traits>
template <class T> using eval = typename T::type;
template <class T> struct identity {using type = T;};
template <typename T, typename... E>
class best_conversion
{
template <typename...> struct overloads {};
template <typename U, typename... Rest>
struct overloads<U, Rest...> :
overloads<Rest...>
{
using overloads<Rest...>::call;
static identity<U> call(U);
};
template <typename U>
struct overloads<U>
{
static identity<U> call(U);
};
template <typename... E_>
static identity<eval<decltype(overloads<E_...>::call(std::declval<T>()))>>
best_conv(int);
template <typename...>
static identity<void> best_conv(...);
public:
using type = eval<decltype(best_conv<E...>(0))>;
};
template <typename... T>
using best_conversion_t = eval<best_conversion<T...>>;
Demo。对于上面“未指定”的情况,此模板会为您提供int
。
基本思想是将一堆带有一个参数的重载函数放入名称查找将查找的不同范围中,每个重载的参数和返回类型对应于列表中的一个类型。
overloads
通过递归地一次引入一个call
声明并通过call
声明调整基本特化中所有先前引入的using
来完成此工作。这样,所有call
都在不同的范围内,但在重载解析方面被认为是同等的。
然后在函数模板best_conv
中应用SFINAE来检查对call
(在overloads
内)的调用是否格式正确:如果是,则采用返回类型(即根据定义,所选声明的参数类型)并将其用作我们的结果 - 它将是我们正在寻找的类型。
还提供best_conv
的第二次重载,返回void
和可以选择作为默认值(当SFINAE应用于第一次重载并将其踢出候选集时)。
使用identity<>
时,返回类型可以避免类型衰减数组或函数指针类型。