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?
答案 0 :(得分:13)
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.
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.
答案 1 :(得分:7)
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.