【问题标题】:Variadic template that determines the best conversion确定最佳转换的可变参数模板
【发布时间】:2014-12-07 01:03:00
【问题描述】:

我们如何实现一个可变参数模板,给定一个类型 T 和一个类型列表 E1E2、... EN,根据重载决议确定从T 到该类型的转换最佳的列表类型?

void 应该是不存在最佳转换的输出 - 换句话说,当存在歧义或T 无法转换为列表中的任何类型时。
请注意,这意味着我们的模板应该对 SFINAE 友好,即在不存在最佳转换时不会出现硬错误。

以下static_asserts 应该会成功:

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>, ???>{}, "" );

这里应该可以接受voidint。 (如果选择后者,那么我们仍然可以在包装模板中检查结果类型是否在列表中包含两次,如果是,则输出void

【问题讨论】:

    标签: c++ type-conversion variadic-templates c++14 overload-resolution


    【解决方案1】:

    我目前最好的方法:

    #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 声明并通过 using 声明从基本专业化中调整所有先前引入的 calls 来完成这项工作。这样一来,所有calls 都在不同的范围内,但在重载决议方面被平等地考虑。

    然后在函数模板best_conv中应用SFINAE来检查对call(在overloads内)的调用是否格式正确:如果是,则取返回类型(根据定义,参数类型)选择声明并将其用作我们的结果 - 这将是我们正在寻找的类型。
    还提供 best_conv 的第二个重载,它返回 void 并且可以选择为默认值(当 SFINAE 适用于第一个重载并将其踢出候选集)。

    返回类型使用identity&lt;&gt; 以避免在使用例如数组或函数指针类型。

    【讨论】:

    • (...) 有点不确定:许多类型都是 UB 传递的。将其设为T&amp;&amp;,... 并删除它?
    • @Yakk 它只是为了取一个int(如果best_conv 的两个重载都留在候选集中,则使其匹配更差),这很好。如果你愿意,我可以long
    • Ag,我明白了:不是declval&lt;U&gt;()。我脑海中的设计与void 案例更直接地与其他重载竞争(使用... 以保证它最后运行),我将它投射到您的不同解决方案上。对不起!
    猜你喜欢
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 2014-04-30
    • 1970-01-01
    相关资源
    最近更新 更多