使用透明的std函数对象时,我们还需要编写空的尖括号吗?

met*_*fox 34 c++ templates language-lawyer argument-deduction c++17

使用类模板参数推导我们可以写:

std::less Fn;
Run Code Online (Sandbox Code Playgroud)

但是,G ++ 8.2拒绝此代码:

#include <algorithm>
#include <vector>
#include <functional>

int main()
{
std::vector v= { 1, 3, 2, 7, 5, 4 };

std::sort(v.begin(),v.end(),std::greater());
}
Run Code Online (Sandbox Code Playgroud)

发出以下错误:

error: cannot deduce template arguments for 'greater' from ()
Run Code Online (Sandbox Code Playgroud)

Clang ++ 7.0和MSVC 15.8.0在没有警告的情况下编译它.哪个编译器是对的?

xsk*_*xzr 28

GCC错了.已有错误报告.

[dcl.type.simple]/2说:

类型说明符的形式的typename选择 嵌套名称说明符选择 模板名称为推导的类型([dcl.type.class.deduct])的占位符.

[dcl.type.class.deduct]/2表示:

一种用于推导的类类型的占位符,也可在所使用的类型说明符-SEQ新型-ID类型-ID一个的新的表达,作为简单型说明符在一个显式的类型转换(功能表示法)([expr.type.conv]),或作为模板参数参数声明中的类型说明符.推断类类型的占位符不应出​​现在任何其他上下文中.

允许这样的使用.


[temp.arg]/4描述了需要template-id语法错误,但没有.但是,此处未作为模板ID解析,因此段落不适用.<>std::greater

  • @metalfox不是吗?[\ [expr.type.conv \]/1](http://eel.is/c++draft/expr.type.conv#1):"一个简单类型说明符或类型名称说明符后跟一个括号可选表达式列表或braced-init-list(初始化程序)在给定初始化程序的情况下构造指定类型的值.如果类型是推导类类型的占位符,则它将替换为函数的返回类型通过重载决议为本子条款的其余部分扣除类模板." (2认同)

son*_*yao 14

Clang和MSVC是正确的.由于隐式生成的演绎指南(自C++ 17)和默认模板参数的组合效果,这应该是格式良好的.

(强调我的)

当函数样式转换或变量声明使用不带参数列表的主类模板C的名称作为类型说明符时,推导将按如下方式进行:

  • 如果定义了C,则对于在命名主模板中声明的每个构造函数(或构造函数模板)Ci(如果已定义),构造虚构函数模板Fi,使得
    • Fi的模板参数是C的模板参数(如果Ci是构造函数模板),Ci的模板参数(也包括默认模板参数)
    • Fi的函数参数是构造函数参数
    • Fi的返回类型是C,后跟<>中包含的类模板的模板参数
  • 如果未定义C或未声明任何构造函数,则添加一个额外的虚构函数模板,如上所述从假设构造函数C()派生
  • 在任何情况下,添加如上从假设构造函数C(C)导出的附加虚构函数模板,称为复制推导候选.

然后执行模板参数推导和重载解析以初始化假设类类型的虚构对象,其构造函数签名与引导(返回类型除外)匹配以形成重载集,并且初始化器由上下文提供.执行了哪个类模板参数推导,除了如果初始化列表由类型的单个表达式(可能是cv-qualified)U组成,则省略列表初始化的第一阶段(考虑初始化列表构造函数),其中U是特化C或从C的专业化派生的类

这些虚构的构造函数是假设类类型的公共成员.如果指南是由显式构造函数构成的,则它们是显式的.如果重载解析失败,则程序格式错误.否则,所选F模板特化的返回类型将成为推导出的类模板特化.

给定std::greater(),应用隐式生成的演绎指南,最后选择附加的虚构函数.作为重载解析的结果,void应用默认参数,然后推导出的类型将是void.这意味着std::greater()应该与写作std::greater<void>()std::greater<>().


顺便说一句:Gcc没有编译std::greater(),但是std::greater{}或者std::greater g;很好,它可能是gcc的错误.