我有typename T1
,我有一个参数包typename... Variadic
。
我想创建一个结构,其中包含使用别名using Type = ...
到参数包中可以转换为T1
的第一个类型。到目前为止,我尝试了以下内容:
template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
using Type = std::enable_if<std::is_convertible<T1, T2>::value, T2>::type;
};
对于前两种类型,这可能是使用SFINAE的潜在解决方案,但我需要使用递归将其扩展到包中的所有类型。到目前为止,我的所有尝试都失败了,因为您无法将条件放入使用别名声明。否则可以使用类似的东西:
template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
using Type = std::is_convertible<T1, T2>::value ? T2 : VariadicConvertibleType<T1, Variadic...>::Type;
};
我可以使用C ++ 14中的所有内容来实现解决方案。我不能使用除标准库之外的任何东西。
答案 0 :(得分:4)
您可以使用std::conditional,如下所示:
template<typename T1, typename T2, typename... Variadic>
struct VariadicConvertibleType
{
using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, typename VariadicConvertibleType<T1, Variadic...>::type>;
};
template<typename T1, typename T2>
struct VariadicConvertibleType<T1, T2>
{
static_assert(std::is_convertible<T1, T2>::value);
using type = T2;
// Alternative base-case
// using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, T1>;
};
我提供了两个基本案例(评论中的备选案例)。
如果static_assert
不能转换为T1
中的任何类型,那么主要的(我想要的是你)会使用(C ++ 14)Variadic
。在这种情况下,替代基本案例会将type
设置为T1
。
<强>测试强>
#include <iostream>
#include <type_traits>
#include <typeinfo>
int main()
{
using my_type_1 = typename VariadicConvertibleType<int, double, float>::type;
std::cout << typeid(my_type_1).name() << '\n'; // double
using my_type_2 = typename VariadicConvertibleType<int, int*, float>::type;
std::cout << typeid(my_type_2).name() << '\n'; // float
using my_type_3 = typename VariadicConvertibleType<int, int*, float*>::type;
std::cout << typeid(my_type_3).name() << '\n'; // Complile error with the primary base-case, and int with the alternative base-case.
}
答案 1 :(得分:0)
std::conditional
的问题在于它需要为您提供的类型定义良好。为了避免这种情况,你需要在这个过程中引入一种间接。
为此,我们将有一个给出布尔值的辅助模板,将定义类型或回退到下一个元素。
namespace details {
template <bool B, typename T, typename... Args>
struct FirstConvertibleImpl;
template <typename T, typename U, typename... Args>
struct FirstConvertibleImpl<true, T, U, Args...> {
using type = U;
};
template <typename T, typename U, typename V, typename... Args>
struct FirstConvertibleImpl<false, T, U, V, Args...> {
using type = typename FirstConvertibleImpl<std::is_convertible<T, V>::value, T, V, Args...>::type;
};
}
如果给定的类型没有导致有效的转换,这显然会发出错误,因为我们并不关心定义&#34;无效&#34;案例(FirstConvertibleImpl<false, T1, T2>
没有任何类型T1, T2
)
现在我们制作更高级别的界面以便更清楚地使用:
template <typename T, typename... Args>
struct FirstConvertibleInPack;
template <typename T, typename U, typename... Args>
struct FirstConvertibleInPack<T, U, Args...> {
using type = typename details::FirstConvertibleImpl<std::is_convertible<T, U>::value, T, U, Args...>::type;
};
同样,我们并不关心定义Args... = []
的案例,因为这些案例无论如何都是错误的。
在Coliru上可以找到Live Demo。
你当然可以更深入地抽象,并通过允许应用你想要的任何二进制特征使这个例子更通用。