在SFINAE中缩小int到bool,gcc和clang之间的输出不同

时间:2014-06-21 20:03:29

标签: c++ gcc c++11 clang sfinae

考虑以下示例:

template<int i>
struct nice_type;

template<class T>
struct is_nice : std::false_type {};

template<int i>
struct is_nice< nice_type<i> > : std::integral_constant<int, i> {};

template<class T, class = void>
struct pick
{
    typedef std::integral_constant<int, -1> type;
};

template<class T>
struct pick<T, typename std::enable_if< is_nice<T>::value >::type >
{
    typedef std::integral_constant<int, is_nice<T>::value > type;
};

int main()
{
    std::cout << pick<int>::type::value << ", ";
    std::cout << pick< nice_type<42> >::type::value << std::endl;
    return 0;
}

Clang(3.4.1)输出&#34; -1,-1&#34;,而GCC(4.9.0)输出&#34; -1,42&#34;。

问题在于pick的专业化。虽然Gcc似乎很乐意将is_nice<T>::value(42)转换为bool(true),但clang没有这样做,并且放弃了专业化。这两个示例都使用-std=c++11编译。

哪种编译器是对的?

1 个答案:

答案 0 :(得分:9)

这是gcc bug 57891。将整数常量42转换为bool涉及缩小转换,这在非类型模板参数中是不允许的。因此,enable_if格式不正确,pick专业化应该被丢弃,正如clang正确的那样。

§14.3.2/ 5 [temp.arg.nontype]

  

对用作a的每个表达式执行以下转换   非类型模板参数。如果不能使用非类型 template-argument   转换为相应的模板参数的类型然后   节目形成不良。
   - 对于非类型模板参数   积分或枚举类型,转换后允许的转换   应用常量表达式(5.19)   ...

§5.19/ 3 [expr.const]

  

...类型为T已转换常量表达式是一个表达式,隐式转换为类型为T的prvalue,其中转换后的表达式为核心   常量表达式和隐式转换序列仅包含用户定义的转换,左值到右值转换(4.1),整数促销(4.5)和积分转换(4.7)除了缩小转化次数(8.5.4)。

§8.5.4/ 7 [dcl.init.list]

  

缩小转化是隐式转化   ...
   - 从整数类型或未范围的枚举类型到不能表示原始类型的所有值的整数类型,除非源是一个常量表达式,其整数提升后的值将适合目标类型。


这个minimal example演示了gcc错误:

template<bool>
struct foo{};
foo<10> f;

int main() {}

gcc-4.9接受代码而clang-3.4拒绝它时出现以下错误:

  

错误:非类型模板参数的计算结果为10,不能缩小为'bool'类型[-Wc ++ 11-narrowing]

 foo<10> f;
     ^

解决您的特定问题很容易。确保enable_if的非类型模板参数的计算结果为bool

template<class T>
struct pick<T, typename std::enable_if< is_nice<T>::value != 0 >::type >
//                                                       ^^^^^^
{
    typedef std::integral_constant<int, is_nice<T>::value > type;
};