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

Struct that contains an alias to the first convertible type of a variadic pack

本文关键字:类型 别名 结构 可转换 第一个 参数 包含可 变参      更新时间:2023-10-16

我有typename T1,我有一个参数包typename... Variadic

我想创建一个结构,其中包含一个 using 别名,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 的所有内容来实现解决方案。不过,除了标准库之外,我不能使用任何其他东西。

你可以使用 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>;
};

我提供了两个基本案例(评论中的替代方案(。 主要的(我的东西是你想要的(使用 (C++14(static_assert如果T1不可转换为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.
}

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

如果给定的类型不会导致有效的转换,这显然会发出错误,因为我们不在乎定义"无效"的情况(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上找到。

当然,您可以更深入地抽象它,并通过允许应用您喜欢的任何二元特征来使此示例更加通用。