Using functors with parameters generally looks like that:
// Definition:
struct some_functor_with_params
{
int ref;
explicit some_functor_with_params(int ref) : ref(ref) {}
bool operator ()(int i) const {return i == ref;}
};
// Usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_with_params(42));
For a functor without parameters, the code may become
// Definition:
struct some_functor_without_params
{
bool operator ()(int i) const {return i == 42;}
};
// Usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_without_params());
but I would prefer the following usage:
std::vector<int> v;
std::find_if(v.begin(), v.end(), some_functor_without_params); // no parens
which has the following advantages:
more concise
more readable when invoking the functor directly: some_functor_without_params(i) rather than some_functor_without_params()(i)
interchangeable with a function: bool some_functor_with_params(int i) {return i == 42;}
I implemented it the following way in a header file:
namespace {
struct
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
}
I think the struct does not need a name since it has no user-declared constructor (nor destructor, nor anything requiring the struct name). I put the object in an unnamed namespace so that each compilation unit has its own some_functor_without_params and there is no 'double definition' link error.
Is there any performance penalty or any other drawback I cannot see?
This approach worked as expected until I encountered a very strange compilation error with Visual C++ 2013 which disappeared when naming the functor type, i.e. replacing
struct
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
with
struct some_functor_without_params_t
{
bool operator ()(int i) const {return i == 42;}
} some_functor_without_params;
The error occurs only in Debug, not in Release, and states
error C2039: '<unnamed-type-some_functor_without_param>': is not a member of 'my_namespace:: ?? A0xbf2cc73f'
in file xstring, here:
_Myt& operator+=(const _Elem *_Ptr)
{ // append [_Ptr, <null>) // <-- on this line(!)
return (append(_Ptr));
}
It looks like a compiler bug, what do you think?
Related
Say I have the following struct:
struct Parameter {
double value;
double error;
};
So that I'm usually working with vectors of that struct (ie. std::vector<Parameter>), and ocasionally I want to set a vector of values (but not errors) in that vector of parameters by using the operator= with a standard std::vector, for convenience.
std::vector<Parameter> vector_of_parameters;
std::vector<double> vector_of values;
....
vector_of_parameters = vector_of_values;
To do so, I'm trying to overload operator= for this struct as follows:
std::vector<Parameter> operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
But this will return an error saying that std::vector operator=(const std::vector& v) must be a non-static member. So if I understand it correctly, I have to define this as a member function of the operator as:
std::vector<Parameter>::operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
The error now says that a syntaxis with template<>, but I dont really see it, or understand it, and don't know what more can I do.
You cannot overload the assignment operator of std::vector. operator = must be a member function and you just can't add a member function to std::vector.
What you can do is make a convenience function like create_parameters that takes a std::vector<double> and returns a std::vector<Parameter>. That would look like
std::vector<Parameter> create_parameters(std::vector<double> const& params)
{
std::vector<Parameter> ret(params.size());
std::transform(params.begin(), params.end(), ret.begin(),
[](auto value) { return Parameter{value, 0}; });
return ret;
}
and then
vector_of_parameters = vector_of_values;
would become
vector_of_parameters = create_parameters(vector_of_values);
I think that an alternative simple way to create std::vector<Parameter> from std::vector<double> is defining a single argument constructor Parameter(double value) which accepts the Parameter::value:
#include <vector>
#include <optional>
struct Parameter
{
double value;
std::optional<double> error;
explicit Parameter(double v) : value(v)
{}
};
Then you can use range-constructor as follows:
DEMO
std::vector<Parameter> v_of_parameters(v_of_values.cbegin(), v_of_values.cend());
Entry.h:
//returns the sum of all non mega entry percentages
float sumOfNonMegaEntryPct(vector<Number>& arg1_Numbers);
Entry.cpp:
//returns the sum of all mega entry percentages
float Entry::sumOfMegaEntryPct(vector<MegaNumber>& arg1_MegaNumbers)
{
float sumPct = 0.00f;
for (MegaNumber c : megaEntry)
{
sumPct = sumPct + arg1_MegaNumbers[c.getID()].getOccurencePct();
}
return sumPct;
}
Lotto.h:
public:
//compares two entries, used for sorting algorithm, sorts by nonmega number
bool compareEntry_sumPct_nonMega(Entry arg1, Entry arg2);
protected:
vector<Numbers> numbers;
vector<MegaNumbers> megaNumbers;
Lotto.cpp:
#include "lotto.h"
//sorts nonmega numbers by sum of their pct, used for sort algorithm
bool Lotto::compareEntry_sumPct_nonMega(Entry arg1, Entry arg2)
{
bool b = arg1.sumOfNonMegaEntryPct(numbers) < arg2.sumOfNonMegaEntryPct(numbers);
return b;
}
Source.cpp:
vector<Entry> copyGameEntry = game.getPlayEntry();
sort(copyGameEntry.begin(), copyGameEntry.end(),
bind(&Lotto::compareEntry_sumPct_nonMega, game));
This is just a part of the code, but I think it's enough to make sense. When compiling, I get the error(s):
Severity Code Description Project File Line Error C2451 conditional
expression of type 'std::_Unforced' is illegal Lottery Sort e:\program
files (x86)\microsoft visual studio 14.0\vc\include\algorithm 3133
Severity Code Description Project File Line Error C2675 unary '!':
'std::_Unforced' does not define this operator or a conversion to a
type acceptable to the predefined operator Lottery Sort e:\program
files (x86)\microsoft visual studio 14.0\vc\include\algorithm 3118
Question:
What could be the problem ?
You are using std::bind incorrectly. You need to use placeholders for the unbound arguments:
using namespace std::placeholders;
sort(copyGameEntry.begin(), copyGameEntry.end(),
bind(&Lotto::compareEntry_sumPct_nonMega, game, _1, _2));
N.B. this bind expression will copy the game object, so you should either use std::ref(game) or just &game instead, to avoid an unnecessary copy.
Or use a lambda function:
sort(copyGameEntry.begin(), copyGameEntry.end(),
[&game](Entry& l, Entry& r) {
return game.compareEntry_sumPct_nonMega(l, r);
});
There are alternatives to invoke std::sort:
#include <algorithm>
#include <vector>
struct X
{
int value;
bool operator < (const X& other) const { return value < other.value; }
static bool less(const X& a, const X& b) { return a.value < b.value; }
};
struct Holder
{
bool less(const X& a, const X& b) const { return a.value < b.value; }
};
int main ()
{
Holder holder;
std::vector<X> values;
// No stateful comparison
std::sort(values.begin(), values.end());
// No stateful comparison
std::sort(values.begin(), values.end(), X::less);
// Stateful comparison
struct Less {
const Holder& holder;
Less(const Holder& holder) : holder(holder) {}
bool operator ()(const X& a, const X& b) const { return holder.less(a, b); }
};
std::sort(values.begin(), values.end(), Less(holder));
// Stateful comparison
std::sort(values.begin(), values.end(), [&holder](const X& a, const X& b) {
return holder.less(a, b);
});
// Stateful comparison
using namespace std::placeholders;
std::sort(values.begin(), values.end(), std::bind(&Holder::less, holder, _1, _2));
}
Likely, in your case you are missing std::placeholders
Your error is related to: bind(&Lotto::compareEntry_sumPct_nonMega, game).
You need to specify std::placeholders when you call function std::bind, which will be replaced by the arguments of the called, returned function object, in the specified order.
You could verify using the following try - catch block:
try {
std::sort(copyGameEntry.begin(), copyGameEntry.end(),
bind(&Lotto::compareEntry_sumPct_nonMega, game)
} catch (std::bad_function_call& e) {
std::cout << "ERROR: Bad function call\n";
}
In your case you need to add:
using namespace std::placeholders;
auto func_obj = bind(&Lotto::compareEntry_sumPct_nonMega, game, _1, _2);
then, the func_obj (_1, _2) will be called, internally by sort(), as:
func_obj(copyGameEntry[i], copyGameEntry[i+1]);
Alternatively, you can try using something like:
struct Holder{
bool less(const Entry& a, const Entry& b) const {
return a.sumOfNonMegaEntryPct(numbers) < b.sumOfNonMegaEntryPct(numbers);
}
} holder;
struct Less {
const Holder& holder;
Less(const Holder& holder) : holder(holder) {}
bool operator ()(const Entry& a, const Entry& b) const { return holder.less(a, b); }
};
std::sort(copyGameEntry.begin(), copyGameEntry.end(), Less(holder));
I am trying to use std::equal_range with the structure below I have compilation error saying that error: no match for ‘operator<’ .
struct MyFoo {
int v_;
string n_;
bool operator<(int v) const
{ return v_ < v;}
};
vector<MyFoo> data;
// data is sorted by int v_
typedef vector<MyFoo>::iterator Ptr;
std::pair< Ptr, Ptr > pr = std::equal_range(data.begin(), data.end(), 10);
I've looked into the template implementatino and what is failing is the following where *it is deferenging the iterator pointing to an object of MyFoo and val_ is 10.
if(*it < val_) {
...
}
Why it is not working? I thought probably because it is trying to call the the global operator< that is not defined, but since I defined it as class member that should not be a problem, isn't it?
Provide non-member comparison operators :
bool operator<(int v, const MyFoo& foo)
{
return foo.v_ < v;
}
bool operator<(const MyFoo& foo, int v)
{
return v < foo;
}
Alternatively, you can provide a conversion operator to int :
operator int() cont {return v_;}
Which is probably unwanted, since the compiler will be able to perform silent conversions in other places of your code.
As an other alternative: provide
bool operator<(const MyFoo& rhs) const { return v_ < rhs.v_; }
And use std::equal_range on a dummy object with correct v_ as:
std::pair<Ptr, Ptr> pr = std::equal_range(data.begin(), data.end(), MyFoo{10, ""});
You may be having trouble because the std::equal_range implementation uses std::less. This is going to try to convert your MyFoo to an int to do the comparison, rather than just using an operator<() overload. Try adding this to your MyFoo class...
operator int() const
{
return v_;
}
I just tried to make a function that compares 2 objects, but it gives me:
Error: bool Duree::operator==(const Duree&, const Duree&) must take exactly one argument
How can I solve this? Thank you.
Duree.h
#ifndef DEF_DUREE
#define DEF_DUREE
class Duree
{
public:
Duree(int heures = 0, int minutes = 0, int secondes = 0);
bool estEgal(Duree const& b) const;
bool operator==(Duree const& a, Duree const& b);
private:
int m_heures;
int m_minutes;
int m_secondes;
};
#endif
Duree.cpp
#include "Duree.h"
Duree::Duree(int heures, int minutes, int secondes) : m_heures(heures), m_minutes(minutes), m_secondes(secondes)
{
}
bool Duree::estEgal(Duree const& b) const
{
return (m_heures == b.m_heures && m_minutes == b.m_minutes && m_secondes == b.m_secondes);
}
bool operator==(Duree const& a, Duree const& b)
{
return a.estEgal(b);
}
Main.cpp
#include <iostream>
#include "Duree.h"
using namespace std;
int main()
{
Duree fisrt(10, 10, 10), second(15, 20);
if (fisrt == second)
cout << "Les durees sont identiques";
else
cout << "Les durees sont differentes";
return 0;
}
Either you declare operator== as a free function with two arguments:
bool operator==(Duree const& a, Duree const& b);
or as a member function with only one argument:
bool Duree::operator==(Duree const& b);
This is because when you do x == y you are comparing only two objects. If you have a member function there's an implicit "this object" (the one you call operator== on) passed, making it 3 arguments instead of 2.
That being said, from the way you wrote the code I'm guessing you just forgot to put friend in front of the operator== declaration, in the class definition.
Probably useful tip: You can use #pragma once, on compilers that support it (basically every "main" compiler), instead of include guards. :)
Change your prototype to bool operator==(Duree const& rhs); or make it a free function out of class Duree.
Always prefer the binary functions to be NON MEMBER (free) functions. Otherwise you can run into problems if you overload the operator with different types. Consider the following contrived code:
struct Foo {
int x;
bool operator==(Foo const & other) const { return x == other.x; }
};
This should work fine for comparing two Foos together. But it has a problem.
Suppose you also want to compare to an int:
struct Foo {
int x;
bool operator==(Foo const & other) const { return x == other.x; }
bool operator==(int other) const { return x == other; }
};
Now you can compare one way but not the other:
Foo a, b;
...
a == b; // ok
a == 123; // ok
123 == a; // ERROR
As member function the object must be on the right hand side.
Simple, move the int-Foo overloads out of the class, and make two versions, Foo==int and int==Foo? (Note, making them friends declared inside the class can accomplish that too, FWIW, but I'm not showing it here.)
struct Foo {
int x;
bool operator==(Foo const & other) const { return x == other.x; }
};
bool operator==(int other, Foo const& f) { return f.x == other; }
bool operator==(Foo const& f, int other) { return f.x == other; }
So now everything works, right? We have a mix of member and non-member operators.
a == b; // ok
a == 123; // ok
123 == a; // ok
Until Foo starts getting member functions that want to use the operators...
struct Foo {
int x;
bool operator==(Foo const & other) const { return x == other.x; }
void g(int x);
};
bool operator==(int other, Foo const& f) { return f.x == other; }
bool operator==(Foo const& f, int other) { return f.x == other; }
void Foo::g(int x)
{
Foo f = getOtherFoo();
if (f == x) { // ERROR! cannot find operator==(Foo,int)
//...
}
}
It STILL has a problem! Inside members of of Foo, it cannot see the non-member operators because the member one hides them! g() will not compile since it can't compare Foo to int. Moving all of the operators out will resolve it, since then all of the overloads are in the same scope.
(Remember, name-lookup keeps searching outer scopes UNTIL it finds the fist case of the name it is looking for, and then only considers all the names it finds in that scope. Inside g(), it's scope is in the class, and since it finds one version of the operator inside the class (the wrong one), it never looks outside the class for more overloads. But if they are all outside the class, it'll find them all at the same time.)
struct Foo {
int x;
void g(int x);
};
// NON MEMBER
bool operator==(Foo const & lhs, Foo const & rhs) { return lhsx == rhs.x; }
bool operator==(int other, Foo const& f) { return f.x == other; }
bool operator==(Foo const& f, int other) { return f.x == other; }
void Foo::g(int x)
{
Foo f = getOtherFoo();
if (f == x) { // OK now, finds proper overload
//...
}
}
Now, it compiles in all the cases. That's why they say, "Always prefer to make non-member binary operator overloads." Otherwise you can end up with symmetry and hiding problems.
I'm on the same kind of issue.
Worked by putting operator== with two arguments back in main.cpp before the main() ;)
Here's a simplified version of my problem. I have a property class. It has data like has_initalized and such which i removed for this example.
When i call a function which uses T its fine. However T& isnt so i decided to write a T& version of it. But this causes all functions which uses plain T to get a compile error. Why is T& interfering with that? For this example how do i get both functions (Q and W) to work without changing main()?
template <class T>
class Property {
T v;
Property(Property&p) { }
public:
Property() {}
T operator=(T src) { v = src; return v; }
operator T() const { return v; }
operator T&() const{ return v; }
T operator->() { return v; }
};
class A{};
void Q(A s){}
void W(A& s){}
int main(){
Property<A> a;
Q(a);
W(a);
}
There is nothing in the overloading rules of C++ which allows the compiler to choose between operatorT() and operatorT&() in the call to Q. So removing the
operator T() const { return v; }
will also remove the ambiguity. But then you'll have a problem because returning a non const reference to a member in a const function is not possible.
For your Q, you can use both conversion functions. You can make the compiler prefer one over the other by making one non-const.
operator T() const { return v; }
operator T&() { return v; }
Now for Q, the operator T& is taken. This way will also fix the call to W to get a non-const reference. You can also return a const reference from the other
operator T const&() const { return v; }
operator T&() { return v; }
This way will still prefer the second conversion function for Q, but if your object a is const and you initialize a const reference, you won't always require to copy v.