c++ - 在存在通用构造函数的情况下,三元运算符上的 SFINAE 失败

标签 c++ gcc ternary-operator sfinae typetraits

我有以下代码,另见 live example :

template <typename A, typename B>
using ternary = decltype(true ? std::declval <A>() : std::declval <B>());

template <template <typename, typename> class F>
struct test
{
    template <typename A, typename B, typename T = F <A, B> >
    static std::true_type call(int);

    template <typename A, typename B>
    static std::false_type call(...);
};

template <template <typename, typename> class F, typename A, typename B>
using sfinae = decltype(test <F>::template call <A, B>(0));

template <typename T> struct X { };

template <typename T> struct Y
{
    template <typename A>
    Y(A&& a) { }
};

int main ()
{
    using A = X <int>;
    using B = X <double>;
    using C = Y <int>;
    using D = Y <double>;
    sfinae <ternary, A, B>{};  // OK
    sfinae <ternary, C, D>{};  // GCC error:
           // operands to ?: have different types ‘Y<int>’ and ‘Y<double>’
}

是对实际代码极度简化的结果,有没有用就别问了。粗略地说,sfinae <ternary, A, B>对是否可以应用三元运算符 ?: 进行了相当标准的 SFINAE 测试到两个类型的参数 A , B .

Clang 编译良好。 GCC 可以使用类模板进行第一次调用 X但在使用 Y 的第二次调用时出错.该错误表明 SFINAE 失败并且编译器意外地尝试应用三元运算符,这是不应该的。 SFINAE 应该永远不会失败(导致编译器错误):它应该总是编译,返回 true_typefalse_type (在这个例子中,它应该总是返回 false_type )。

类模板之间的唯一区别X , Y是那个Y有构造函数

template <typename A>
Y(A&& a) { }

在我的实际代码中,构造函数比较多,都配备了enable_if允许消除歧义。更现实一点,Y会是

template <typename T> struct Y
{
    using type = T;

    template <
       typename A,
       typename = typename std::enable_if <
          std::is_constructible<T, typename A::type>{}
       >::type
    >
    Y(A&& a) : /*...*/ { }
};

允许其他人施工Y有不同的 T ,但关于错误没有任何改变。通常情况下,三元运算符要么由于类型不同而失败,要么由于通用构造函数而模棱两可(是这种情况吗?),但无论哪种方式,SFINAE 都应该报告 false_type .

这个构造函数是如何让 SFINAE 失败的?这里是否符合 GCC 标准?任何解决方法?


编辑

如果我手动实例化

decltype(true ? std::declval <C>() : std::declval <D>());

main() , clang 说

error: conditional expression is ambiguous; 'Y<int>' can be converted to 'Y<double>'
       and vice versa

同时为 A , B它说

incompatible operand types ('X<int>' and 'X<double>')

并且 GCC 在这两种情况下都坚持与上述相同的错误。我不知道这是否有帮助。

最佳答案

这听起来很像 GCC 错误。使用最新版本的 GCC 4.9 会产生此错误:

sftern.cpp: In instantiation of ‘struct test<ternary>’:
sftern.cpp:17:57:   required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: call<A ...>(0)) [with F = ternary; A = {X<int>, X<double>}]’
sftern.cpp:33:26:   required from here
sftern.cpp:10:27: error: pack expansion argument for non-pack parameter ‘A’ of alias template ‘template<class A, class B> using ternary = decltype ((true ?  declval<A>() : declval<B>()))’
     static std::true_type call(int);
                           ^
sftern.cpp:3:11: note: declared here
 template <typename A, typename B>
           ^

听起来你描述的错误已经修复,但它仍然被绊倒。 (只要元素数量正确,将一个包扩展成一个非包是没有问题的。)

关于c++ - 在存在通用构造函数的情况下,三元运算符上的 SFINAE 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22649708/

相关文章:

c - 函数在 'gcc -O2' 优化为无限循环

linux - 将 bash 脚本嵌入到 makefile 中

swift - 如何修复 '? :' 表达式中的结果值具有不匹配的类型 'String' 和 '[Any]?'

java - 条件(三元)运算符代码风格

c++ - 如何获取直接连接到我的PC的计算机的IP地址

c++ - 从文件设计中加载对象

c++ - 从另一个构造函数调用默认构造函数

c++ - 类模板偏特化

c++ - 如何更正警告 : cast to pointer from integer of different size [-Wint-to-pointer-cast]

PHP 三元运算符错误