2015-09-25 118 views
2

这就是我想要的,一个“开关”型特征,它返回具有满足的条件==第一种类型:实现开关型特征(与标准:: conditional_t链话费)

ext::select_t<condition1 == true, Type1, 
       condition2 == true, type2, 
       condition3 == true, type3> 

等,并且能够根据需要添加尽可能多的条件/类型对。

我可以用的std ::条件本身(随便举个例子)做到这一点:

template<typename Number, 
     typename Distribution = std::conditional_t< 
       // IF 
       std::is_integral<Number>::value, 
       // RETURN INT 
       std::uniform_int_distribution<Number>, 
       // ELSE 
       std::conditional_t<std::is_floating_point<Number>::value, 
            // RETURN REAL 
            std::uniform_real_distribution<Number>, void>>> 

Number random(Number min, Number max) 
{ 
    static std::random_device rd; 
    static std::mt19937 mt(rd()); 

    Distribution dist(min, max); 

    return dist(mt); 
} 

,你可以看到它决定在我想根据传递的条件/类型是什么样的分布编译时间。

显然,如果我尝试添加更多的条件,这可以得到真正的丑陋真实的快速,想象我想要其中的10个。

,所以我试图建立一个,但悲惨地失败了:

template<bool B, typename T> 
struct cond 
{ 
    static constexpr bool value = B; 
    using type = T; 
}; 

template<typename Head, typename... Tail> 
struct select 
{ 
    using type = std::conditional_t<Head::value, typename Head::type, select<Tail...>>; 
}; 

template<typename Head> 
struct select<Head> 
{ 
    using type = std::conditional_t<Head::value, typename Head::type, void>; 
}; 

template<typename Head, typename... Tail> 
using select_t = typename select<Head, Tail...>::type; 

我试图使该链表的结构是这样我就可以得到条件/类型“对”的原因,所以我可以得到任何数那些使用可变参数模板,但是这使得它更丑陋的(和不工作):

using Type = std::select_t<cond<false, void>, 
          cond<false, int>, 
          cond<true, std::string>>; 

不仅看起来不那么好,因为我想最后的版本是,但它甚至不工作!它只在第一个条件为真时才起作用。

有什么可以遗漏吗?我怎么能以更干净的方式实现这一目标(至少对最终用户而言)。

在此先感谢。

+0

如果没有一个条件是真的会怎样? – jrok

+0

如果没有,它只是返回void。显然,如果没有真正的条件,最好的解决方案就是编译失败。取决于最终用户的做法,void将(几乎)总是使编译失败,但它可以以某种方式编译正确并在代码中产生错误,不知道如何解决这个问题。 – sap

回答

4

的问题是在你的基本情况:

using type = std::conditional_t<Head::value, typename Head::type, select<Tail...>>; 

你想成功(Head::value)使用的头型(Head::type),但没有使用尾部类型。但select<Tail...>不是尾巴类型。这是一个元函数。要真正地评价它:

using type = std::conditional_t< 
       Head::value, 
       typename Head::type, 
       typename select<Tail...>::type>; 

现在,这是有点低效率的,因为你必须处理条件往上顶的整体。为此,您可以编写一个单独的元函数,Boost.MPL称为eval_if。而不是采取一个布尔和两种类型的,它需要一个布尔两元函数:

template <bool B, typename TrueF, typename FalseF> 
struct eval_if { 
    using type = typename TrueF::type; 
}; 

template <typename TrueF, typename FalseF> 
struct eval_if<false, TrueF, FalseF> { 
    using type = typename FalseF::type; 
};  

template <bool B, typename T, typename F> 
using eval_if_t = typename eval_if<B, T, F>::type; 

随着这您的select主要情况变为:

template<typename Head, typename... Tail> 
struct select 
{ 
    using type = eval_if_t<Head::value, 
          Head, 
          select<Tail...>>; 
}; 

虽然反射,同样可以与实现std::conditional_t和继承:

template <typename Head, typename... Tail> 
struct select 
: std::conditional_t<Head::value, Head, select<Tail...>> 
{ }; 

而且,通常我们只是在结尾处的“其他人”的情况下,所以也许你会写你的选择是:

using T = select_t<cond<C1, int>, 
        cond<C2, float>, 
        double>; 

所以我建议正是如此写你的基本情况:

template <typename T> 
struct select<T> 
{ 
    using type = T; 
}; 

template <bool B, typename T> 
struct select<cond<B, T>> 
{ 
    // last one had better be true! 
    static_assert(B, "!"); 
    using type = T; 
}; 

而且,你写了std::select_t ...不要把它放在命名空间std中,把它放在你自己的命名空间中。

+0

另一种评论:否则“返回也是一个好主意,我甚至可以让它返回一个默认值。但最后一个问题:没有一个容纳一对的cond 结构,我无法做到这一点吗?即使使用2个可变参数模板参数?我也尝试过,但无法使其工作。无论如何,再次感谢。 – sap

+1

@sap对于多个模板参数,它们都必须是类型。你必须传递'std :: true_type'而不是'true'。否则,实现是相同的 - 只需剥离包中的两个参数而不是一个参数。 – Barry

+0

谢谢@Barry生病后来尝试,作为一个练习,非常感谢帮助。 – sap