我正在使用以下SFINAE模式来评估可变参数类型列表上的谓词:
#include <type_traits>
void f(int = 0); // for example
template<typename... T,
typename = decltype(f(std::declval<T>()...))>
std::true_type check(T &&...);
std::false_type check(...);
template<typename... T> using Predicate = decltype(check(std::declval<T>()...));
static_assert(!Predicate<int, int>::value, "!!");
static_assert( Predicate<int>::value, "!!");
static_assert( Predicate<>::value, "!!"); // fails
int main() {
}
令我惊讶的是,选择省略号过载时check
被调用空的参数列表,因此Predicate<>
是std::false_type
即使SFINAE表达是有效的!
可变参数功能模板不应该总是被省略号功能所替代吗?
有什么解决方法吗?
当T...
为空时,编译器执行重载解析以确定哪个
std::true_type check(); // instantiated from the function template
std::false_type check(...);
如[over.match.best] 13.3.3 / 1(引用N3936)所述,是最佳可行候选者:
定义ICSi(F)如下:
如果F是静态成员函数,则对ICS1(F)进行定义,以使ICS1(F)对于任何函数G都不比ICS1(G)更好或更差,并且对称地,ICS1(G)既不比ICS1更好也不差。 (F)132;否则,
让ICSi(F)表示将列表中的第i个参数转换为可行函数F的第i个参数的类型的隐式转换序列。13.3.3.1定义隐式转换序列,而13.3.3.2定义其含义一个隐式转换序列比另一个隐式转换序列更好或更差。
给定这些定义,如果对于所有自变量i,ICSi(F1)的转换顺序都不比ICSi(F2)差,则将一个可行函数F1定义为比另一个可行函数F2更好的函数,然后
对于某些参数j,ICSj(F1)比ICSj(F2)更好,或者,如果不是,
上下文是通过用户定义的转换进行初始化(请参见8.5、13.3.1.5和13.3.1.6),并且从F1的返回类型到目标类型(即,正在初始化的实体的类型)的标准转换顺序为从F2的返回类型到目标类型的标准转换序列比标准的转换序列更好。[示例:
struct A { A(); operator int(); operator double(); } a; int i = a; // a.operator int() followed by no conversion // is better than a.operator double() followed by // a conversion to int float x = a; // ambiguous: both possibilities require conversions, // and neither is better than the other
—结束示例],如果不是这样,
上下文是通过转换函数进行的初始化,用于对函数类型的引用进行直接引用绑定(13.3.1.6),F1的返回类型与要初始化的引用是同一类型的引用(即,左值或右值),并且返回F2的类型不是[示例:
template <class T> struct A { operator T&(); // #1 operator T&&(); // #2 }; typedef int Fn(); A<Fn> a; Fn& lf = a; // calls #1 Fn&& rf = a; // calls #2
—结束示例],如果不是这样,
F1不是功能模板专业化,而F2不是功能模板专业化,或者如果不是,
- F1和F2是功能模板专业化,根据14.5.6.2中描述的部分排序规则,F1的功能模板比F2的模板更专业。
在这种情况下,两个候选者的转换序列为空,因为没有参数。倒数第二个是决定因素:
- F1不是功能模板专业化,而F2不是功能模板专业化,或者如果不是,
因此,非模板std::false_type check(...);
是首选。
我更喜欢的解决方法-显然有很多方法-都是制作候选模板,并通过省略号转换来区分[over.ics.ellipsis] 13.3.3.1.3 / 1:
当函数调用中的参数与所调用函数的省略号参数规范匹配时,将发生省略号转换序列(请参阅5.2.2)。
通过为“首选”模板声明提供一个明显更好匹配的无关参数,因为根据[over.ics.rank] 13.3.3.2/2,任何其他转换顺序都将被优先于省略号转换:
比较隐式转换序列的基本形式时(如13.3.3.1所定义)
- 标准转换序列(13.3.3.1.1)比用户定义的转换序列或省略号转换序列更好,并且
- 用户定义的转换序列(13.3.3.1.2)比省略号转换序列(13.3.3.1.3)更好。
范例:
template<typename... T,
typename = decltype(f(std::declval<T>()...))>
std::true_type check(int);
template<typename...>
std::false_type check(...);
template<typename... T> using Predicate = decltype(check<T...>(0));
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句