包含可变参数包的第一个可转换类型的别名的Struct

时间:2017-07-31 13:04:33

标签: c++ metaprogramming

我有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中的所有内容来实现解决方案。我不能使用除标准库之外的任何东西。

2 个答案:

答案 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

你当然可以更深入地抽象,并通过允许应用你想要的任何二进制特征使这个例子更通用。