什么是C ++元函数的评估策略(渴望,懒惰......),例如std :: conditional?

时间:2016-12-21 16:45:31

标签: c++ metaprogramming template-meta-programming operator-precedence evaluation-strategy

C ++ 14草案n4140读取

  

T应为枚举类型

代表template <class T> struct underlying_type

写下来有多糟糕

std::conditional_t<std::is_enum<T>::value, std::underlying_type_t<T>, foo>

T可以是任意类型吗?我是否会进入UB并且编译器会删除我的$ HOME(因为语言律师说“任何事情都可以在UB下发生”)?

1 个答案:

答案 0 :(得分:4)

  

我会踏上UB [...]

技术上,是的。但实际上,它只是不会为非枚举类型编译。当你写:

std::conditional_t<std::is_enum<T>::value, std::underlying_type_t<T>, foo>;    
                                           ^^^^^^^^^^^^^^^^^^^^^^^^^

conditional模板可以实例化之前,必须对评估该模板参数。这相当于在函数体开始之前必须调用的所有函数参数。对于非枚举类型,underlying_type<T>不完整(确定它在标准中指定为未定义但是合理),因此没有underlying_type_t。因此实例化失败了。

在这种情况下,您需要做的是延迟实例化:

template <class T> struct tag { using type = T; };

typename std::conditional_t<
    std::is_enum<T>::value,
    std::underlying_type<T>,
    tag<foo>>::type;

现在,我们的conditional而非选择类型正在选择元功能! underlying_type<T>::type只会被T实例化为枚举。我们还必须包装foo以将其转换为元函数。

这是一种常见的模式,在Boost.MPL中称为eval_if是一个特殊的东西,它看起来像:

template <bool B, class T, class F>
using eval_if_t = typename std::conditional_t<B, T, F>::type;

请注意,我们都使用conditional_t ::type