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

sba*_*bbi 10 c++ gcc clang sfinae c++11

请考虑以下示例:

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;
}
Run Code Online (Sandbox Code Playgroud)

Clang(3.4.1)输出"-1,-1",而GCC(4.9.0)输出"-1,42".

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

哪个编译器是对的?

Pra*_*ian 9

这是gcc bug 57891.积分常数的转换42bool涉及收缩转换,其未在非类型模板参数允许.因此,这enable_if是不pick正确的,应该放弃专业化,正如clang正确的那样.

§14.3.2/ 5 [temp.arg.nontype]

对用作非类型模板参数的每个表达式执行以下转换.如果非类型模板参数无法转换为相应模板参数的类型,则程序格式错误.
- 对于整数或枚举类型的非类型模板参数,将应用转换常量表达式(5.19)中允许的转换.
...

§5.19/ 3 [expr.const]

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

§8.5.4/ 7 [dcl.init.list]

缩小转换是隐式转换
...
-从一个整数型或无作用域枚举类型为整数类型,它可以不表示原始类型的所有值,除了其中源是一个常量表达式积分优惠后,其值将装配到目标类型.


这个最小的例子演示了gcc bug:

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

int main() {}
Run Code Online (Sandbox Code Playgroud)

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

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

 foo<10> f;
     ^
Run Code Online (Sandbox Code Playgroud)

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

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;
};
Run Code Online (Sandbox Code Playgroud)