Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't initialize std::function with comparator

Tags:

c++

c++11

In my C++ class we are learning to use function objects and the like, but now we got a code snippet that works on teacher's compiler but not on ours (we use different OS's).

We tested the code snippet below with several compilers (MSVC, clang) and they all reject it, a bit minimized:

#include <functional>

struct Fraction {
  Fraction();
  Fraction(int z, int n);
  Fraction(Fraction&);

  // various data members
};

struct FractionComparator {
  int operator()(Fraction a, Fraction b) {
    return 1;
  }
};

int main() {
  std::function<int(Fraction, Fraction)> comparator = FractionComparator();
}

We get on clang on macOS:

No viable conversion from 'FractionComparator' to 'function<int (Fraction, Fraction)>'

We already found out that adding a move constructor solves the problem, but we have no idea why this difference exists and why this code doesn't compile on our compilers.

Any ideas?

like image 339
ForceHobbit Avatar asked Jun 04 '18 17:06

ForceHobbit


People also ask

How to use lambda function as comparator?

We use lambda functionas comparator. As usual, comparator should return boolean value, indicating whether the element passed as first argument is considered to go before the second in the specific strict weak orderingit defines. Online demo 2. Modern C++11 solution auto cmp = [](int a, int b) { return ... }; std::set<int, decltype(cmp)> s(cmp);

What is set in C++ with custom comparator?

C++ Set With Custom Comparator. std::set is a C++ STL container that store unique elements following a specific order. It is defined in the set header file. Benefits and Features of std::set[3]:

How does the comparator function operator () class work?

Explanation: The above comparator function operator () class take two pair of objects at a time and return true if data members of the two operators are the same. There can be any condition as per the need of the problem in the comparator function. In the above example, the function returns true if data members are the same.

What is Comparator interface in C++?

Comparator interface is used to order the objects of user-defined classes. A comparator object is capable of comparing two objects of two different classes. Following function compare obj1 with obj2. Syntax: public int compare(Object obj1, Object obj2):


2 Answers

Why does adding a move-constructor solve the problem?

Fraction is attempted to be copy-constructed from an rvalue.

But constuctor Fraction(Fraction&); takes a non-constant reference. Non-const references are not allowed to bind to temporaries. The proper constructor signature should be:

Fraction(const Fraction&);

When you declare a move constructor compiler will move-construct Fraction from an rvalue instead.

Why doesn't this code compile on our compilers, but compiles on the teacher's?

This code compiles with VC++. It looks like the compiler is not conforming to the standard here. I could find this StackOverflow question with some more detail. It seems to be a compiler extension that allows this to compile. If compiler extensions are disabled via /Za flag it will not compile anymore.

like image 108
AMA Avatar answered Oct 11 '22 04:10

AMA


Starting from C++14 the constructor you are trying to use does not participate in overload resolution unless the target function is callable with the given set of arguments (with std::declval<>() arguments of given types). In your case that would be std::declval<Fraction>() arguments.

Your FractionComparator functor is not callable with std::declval<Fraction>() arguments since it receives its arguments by value, while temporary objects of type Fraction cannot be copied: Fraction's copy-constructor's argument is declared as a non-const reference.

Declare your copy constructor as

Fraction(const Fraction &);

and the code will compile. If you have a good reason to keep it as non-const, then you will have to explore other opportunities for making this work. Why does your FractionComparator insist on receiving its parameters by value anyway?

Your question is tagged C++11, but apparently your compiler is retroactively implementing this C++14 requirement even in C++11 mode.

like image 24
AnT Avatar answered Oct 11 '22 02:10

AnT