我有以下代码,另请参阅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>
对是否可以将三元运算符?:
应用于A
,B
类型的两个参数进行了非常标准的SFINAE测试。
Clang编译好。使用类模板X
进行第一次调用时,GCC正常,但使用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坚持在两种情况下都出现与上述相同的错误。我不知道这是否有帮助。
答案 0 :(得分:1)
这听起来很像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>
^
听起来你描述的错误已被修复,但它仍然被绊倒了。 (只要项目数量正确,将包装扩展为非包装就没问题了。)
答案 1 :(得分:0)
我稍微改写了这个例子,我开始在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
还是我错过了什么?