我有以下代码,另见 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_type
或 false_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/