Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do we still need to write the empty angle brackets when using transparent std function objects?

With class template argument deduction we can write:

std::less Fn;

However, G++ 8.2 rejects this code:

#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());
}

emitting the following error:

error: cannot deduce template arguments for 'greater' from ()

Clang++ 7.0 and MSVC 15.8.0 compile it without warnings. Which compiler is right?

like image 399
metalfox Avatar asked Nov 02 '18 07:11

metalfox


People also ask

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.

What is the function of STD in C++?

Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

Is STD function a pointer?

No. One is a function pointer; the other is an object that serves as a wrapper around a function pointer. They pretty much represent the same thing, but std::function is far more powerful, allowing you to do make bindings and whatnot.

Is std :: function slow?

Code wrapped into std::function is always slower than inlining code directly into calling place. Especially if your code is very short, like 3-5 CPU instructions.


2 Answers

GCC is wrong. There is already a bug report.

[dcl.type.simple]/2 says:

A type-specifier of the form typenameoptnested-name-specifieropttemplate-name is a placeholder for a deduced class type ([dcl.type.class.deduct]).

And [dcl.type.class.deduct]/2 says:

A placeholder for a deduced class type can also be used in the type-specifier-seq in the new-type-id or type-id of a new-expression, as the simple-type-specifier in an explicit type conversion (functional notation) ([expr.type.conv]), or as the type-specifier in the parameter-declaration of a template-parameter. A placeholder for a deduced class type shall not appear in any other context.

Such use is allowed.


[temp.arg]/4 describes the syntax error that a template-id is required but there is no <>. However here std::greater is not resolved as a template-id so that paragraph does not apply.

like image 73
xskxzr Avatar answered Oct 21 '22 12:10

xskxzr


Clang and MSVC are correct. This should be well-formed because of the combination effect of implicitly-generated deduction guides (since C++17) and default template argument.

(emphasis mine)

When a function-style cast or declaration of a variable uses the name of a primary class template C without an argument list as the type specifier, deduction will proceed as follows:

  • If C is defined, for each constructor (or constructor template) Ci declared in the named primary template (if it is defined), a fictional function template Fi, is constructed, such that
    • template parameters of Fi are the template parameters of C followed (if Ci is a constructor template) by the template parameters of Ci (default template arguments are included too)
    • the function parameters of Fi are the constructor parameters
    • the return type of Fi is C followed by the template parameters of the class template enclosed in <>
  • If C is not defined or does not declare any constructors, an additional fictional function template is added, derived as above from a hypothetical constructor C()
  • In any case, an additional fictional function template derived as above from a hypothetical constructor C(C) is added, called the copy deduction candidate.

Template argument deduction and overload resolution is then performed for initialization of a fictional object of hypothetical class type, whose constructor signatures match the guides (except for return type) for the purpose of forming an overload set, and the initializer is provided by the context in which class template argument deduction was performed, except that the first phase of list-initialization (considering initializer-list constructors) is omitted if the initializer list consists of a single expression of type (possibly cv-qualified) U, where U is a specialization of C or a class derived from a specialization of C.

These fictional constructors are public members of the hypothetical class type. They are explicit if the guide was formed from an explicit constructor. If overload resolution fails, the program is ill-formed. Otherwise, the return type of the selected F template specialization becomes the deduced class template specialization.

Given std::greater(), the implicitly-generated deduction guide is applied and the additional fictional function is selected at last. As the result of overload resolution the default argument void is applied, then the deduced type will be void. That means std::greater() should be same as writing std::greater<void>() or std::greater<>().


BTW: Gcc doesn't compile with std::greater(), but std::greater{} or std::greater g; are fine, it might be gcc's bug.

like image 41
songyuanyao Avatar answered Oct 21 '22 11:10

songyuanyao