当不带参数调用时,为什么省略号优于可变参数模板?

恶魔

我正在使用以下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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

可变参数模板和省略号有什么区别?

省略号作为可变参数模板之外的构造函数参数

用可变参数模板功能包装基于省略号的功能

不带参数的可变参数模板的别名

为什么这个嵌套的可变参数模板是无效的参数?

为什么这个可变参数模板是错误的?

为什么clang拒绝可变参数模板的朋友功能

为什么constexpr在可变参数模板中不是常量?

为什么可变参数模板在C ++中的行为如此?

对不带参数的可变参数模板函数的模棱两可的调用?

为什么仅针对一个参数,可变参数模板与非可变模板不同?

R:为什么在省略号是最后一个参数时不提供省略号(...)的列表?

为什么模板参数推导因std :: function回调的可变参数模板参数而失败?

基准可变参数模板函数调用

可变参数模板函子调用

可变参数模板对

为什么可变参数模板参数的这种替换失败?(在固定参数之前打包)

调用作为参数接收的可变参数模板的可变参数模板

嵌套函数调用和省略号的名称相同的参数

为什么我要把省略号放在函数的参数中?

使用嵌套函数时,为什么 R 省略号 (...) 匹配更多参数?

使用可变参数类模板的模板参数调用可变参数函数模板?

为什么在迭代可变参数模板参数时必须使用其他构造?

为什么我的可变参数模板参数验证程序在编译时拒绝评估?

为什么 C++ 可变参数模板不接受 iostream 值作为参数?

为什么可变参数模板参数推导对此函数指针失败?

为什么可变参数模板参数包没有扩展?

С++可变参数模板:实现可变参数

可变参数模板类的可变参数模板