在存在泛型构造函数的情况下,对三元运算符的SFINAE失败
SFINAE on ternary operator fails in the presence of generic constructor
我有以下代码,参见另一个实例:
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) : /*...*/ { }
};
允许其他具有不同T
的Y
构造,但对于错误没有任何改变。通常情况下,三元运算符应该要么由于不同的类型而失败,要么由于泛型构造函数而不明确(情况是这样吗?),但无论哪种方式,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 bug。使用最新版本的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>
^
听起来你描述的bug已经被修复了,但是它仍然会出错。(只要道具数量正确,将一个包扩展为一个非包是没有问题的。)
我稍微改变了一下这个例子,我开始在clang中得到错误:
template <typename A, typename B>
struct ternary1
{
typedef decltype(true ? std::declval <A>() : std::declval <B>()) type;
};
template <template <typename, typename> class F>
struct test1
{
template <typename A, typename B, typename T = typename F <A, B>::type >
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 sfinae1 = decltype(test1 <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>{}; //clang: Incompatible operand types ('X<int>' and 'X<double>')
sfinae <ternary, C, D>{}; //clang: Conditional expression is ambiguous; 'Y<int>' can be converted to 'Y<double>' and vice versa
}
是ternary
相当于typename ternary1::type
或我错过了什么?
- 三元运算符和 if constexpr
- 在 c++ 中三元运算符中不允许继续(关键字)吗?
- 三元运算符在返回语句中给出意外的结果
- 递归中三元运算符的奇怪行为
- 错误:三元运算符无法在对象中正常工作"cout"
- 是否允许三元运算符在C++中计算两个操作数?
- 有条件地选择带有 decltype() 和三元运算符的类型
- 三元运算符在C++中的意外行为
- 有没有办法将 for 循环结果返回到像三元运算符这样的函数中?
- 变量值,在三元运算符之后
- 是否可以在C++中使用三元运算符在 if 语句中选择比较运算符?
- c++中的增量和三元运算符优先级
- C++中三元运算符的意外行为
- 用于返回开关的三元运算符
- 替换模板元编程中的三元运算符
- 三元运算符 '?:' 在 4.9.0 之前的 GCC 版本中推断出不正确的类型?
- 在 std::map 上使用三元运算符和 std::更大的附加参数
- "Do nothing"在三元运算符的其他部分?
- 如何确定三元运算符的返回类型?
- 无法使用三元运算符有条件地分配"istream &"?