c++ - 为什么在没有参数的情况下调用省略号而不是可变参数模板?

标签 c++ templates c++11 variadic-templates sfinae

我正在使用以下 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)中所述:

Define ICSi(F) as follows:

  • if F is a static member function, ICS1 (F) is defined such that ICS1 (F) is neither better nor worse than ICS1 (G) for any function G, and, symmetrically, ICS1 (G) is neither better nor worse than ICS1 (F)132; otherwise,

  • let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

  • the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type. [ Example:

    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
    

    end example ] or, if not that,

  • the context is an initialization by conversion function for direct reference binding (13.3.1.6) of a reference to function type, the return type of F1 is the same kind of reference (i.e. lvalue or rvalue) as the reference being initialized, and the return type of F2 is not [ Example:

    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
    

    end example ] or, if not that,

  • F1 is not a function template specialization and F2 is a function template specialization, or, if not that,

  • F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

在这种情况下,两个候选的转换序列都是空的,因为没有参数。倒数第二个项目符号是决定因素:

  • F1 is not a function template specialization and F2 is a function template specialization, or, if not that,

因此非模板 std::false_type check(...); 是首选。


我首选的解决方法 - 显然有很多 - 是通过省略号转换 [over.ics.ellipsis] 13.3.3.1.3/1 来制作候选模板并进行区分:

An ellipsis conversion sequence occurs when an argument in a function call is matched with the ellipsis parameter specification of the function called (see 5.2.2).

通过为“首选”模板声明提供一个明显更好匹配的无关参数,因为根据 [over.ics.rank] 13.3.3.2/2,任何其他转换序列都将优于省略号转换:

When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)

  • a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
  • a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).

Example :

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));

关于c++ - 为什么在没有参数的情况下调用省略号而不是可变参数模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23804312/

相关文章:

javascript - 针对Win32 x64编译后,Electron应用程序将无法打开

c++ - Visual Studio : Link executable

c++ - 调试绘制 Box2D 与 Testbed 应用程序相比非常大

c++11 - std::thread::id 跨进程是否唯一?

c++ - 为什么关键字 `explicit` 不适用于函数参数?

c++ - 如何防止时间戳被重新排序?

C++ - 将文本追加到仅具有 NTFS "append data"权限的文件

c++ - 侵入式数据结构中的成员钩子(Hook)与基本钩子(Hook)

css - 提醒系统模板

javascript - 如何将 CSS 样式应用于 AJAX 调用后呈现的内容