I am trying to create a multimap using multikey structure as a key and I am getting a error described below:
code:
struct stCont
{
long long Tok;
char Reserved;
long long Asset;
}
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
}
struct myComp
{
bool operator() (const MultiKey& lhs, const MultiKey& rhs)
{
if((lhs.ExpiryDate==rhs.ExpiryDate)&&(memcmp(lhs.InstrumentName,rhs.InstrumentName,6))&&(memcmp(lhs.Symbol,rhs.Symbol,10)))
{
return 1;
}
return 0;
}
};
std::multimap<MultiKey, stCont,myComp> cont_map;
error:
expression having type 'const myComp' would lose some const-volatile qualifiers in order to call 'bool myComp::operator ()(const MultiKey &,const MultiKey &)'
you should rewrite the multimap code like this and remove the mycomp structure:
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
bool operator< (const MultiKey& lhs)const
{
if((lhs.ExpiryDate==ExpiryDate)&&(memcmp(lhs.InstrumentName,InstrumentName,6))&&(memcmp(lhs.Symbol,Symbol,10)))
{
return true;
}
return false;
}
};
Why don't you just write just write operator < for MultiKey? Or you'll have to change myComp because it isn't what multimap wants anyway (it wants a less-than comparison).
Look at the The C++11 Standard, §23.4.5.1 and the header:
template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class multimap {
public:
// ...
class value_compare {
friend class multimap;
protected:
Compare comp;
value_compare(Compare c) : comp(c) { }
public:
typedef bool result_type;
typedef value_type first_argument_type;
typedef value_type second_argument_type;
bool operator()(const value_type& x, const value_type& y) const {
return comp(x.first, y.first);
}
};
// ...
};
The comparison function as defined by class value_compare is const. Now, I may be misinterpreting the standard, but this definition seems to be invalid if the operator() is non-const in class Compare.
As to why does it work for some people... Perhaps some finer points about instantiation rules prevent this from being an error, or the implementations are not requierd to adhere strictly to the type definitions in the standard; if so, I'd be glad if someone more versed with The Standard could clarify.
To fix the compile error, declare the comparison function to be a const member:
bool operator() (const MultiKey& lhs, const MultiKey& rhs) const
^^^^^
Then you have another problem: the comparator needs to perform a "less than" comparison, but yours does an equality comparison. You want something like
if (lhs.ExpiryDate < rhs.ExpiryDate) return true;
if (lhs.ExpiryDate > rhs.ExpiryDate) return false;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) < 0) return true;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) > 0) return false;
if (memcmp(lhs.SymbolName,rhs.SymbolName,10) < 0) return true;
return false;
You may find it more convenient to overload operator< rather than defining a named comparator type, so that you can use std::multimap<MultiKey, stCont> with the default comparator.
Related
I am trying to use the std::tie to implement the operator< in order to create a map of structs that contain a set. The same code without templates seem that it works. I am getting this message code from my compiler:
/usr/include/c++/4.8/bits/stl_algobase.h:888: error: no match for operator< (operand types are const SiPa<int, int> and const SiPa<int, int>)
if (*__first1 < *__first2)
^
Everything compiles if I comment the myMap.insert({akey, true}); line.
Any hints?
template<class I = int, class S = int>
struct SiPa
{
I i;
S s;
};
template<class I = int, class S = int>
struct SiPaComparator
{
bool operator() (const SiPa<I, S>& first, const SiPa<I, S>& second) const
{
return std::tie(first.i, first.s) < std::tie(second.i, second.s);
}
};
template<class I = int, class S = int>
struct AKey
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
};
template<class I = int, class S = int>
struct AKeyComparator
{
bool operator() (const AKey<I, S>& first, const AKey<I, S>& second) const
{
return std::tie(first.keySet, first.keyI) < std::tie(second.keySet, second.keyI);
}
};
int main()
{
AKey<int,int> akey;
std::map<AKey<int,int>, bool, AKeyComparator<int,int>> myMap;
myMap.insert({akey, true});
}
You need add operator< for struct SiPa, std::map require it
template<class I = int, class S = int>
struct SiPa {
I i;
S s;
bool operator<(const SiPa<I, S> &ref) {
return i < ref.i && s < ref.s;
}
};
In general, comparators on maps and sets are stateful. When comparing two different sets or maps, there is no obvious way to pick which one to use.
So when comparing different sets and maps via <, you get std::lexographical_compare with no Compare argument, which uses <. (Note this sucks for sets of pointers to objects not from the same array)
struct order_by_tie {
template<class Lhs, class Rhs,
class=std::enable_if_t<
std::is_base_of<order_by_tie, Lhs>::value
&& std::is_base_of<order_by_tie, Rhs>::value
>
>
friend bool operator<(Lhs const& lhs, Rhs const& rhs) {
return as_tie(lhs) < as_tie(rhs);
}
};
order_by_tie is intended to be inherited from. It uses ADL (argument dependent lookup) to enable < on its descendent classes, implemented by calling the free function as_tie on each side then doing a <.
We use it as follows:
template<class I = int, class S = int>
struct SiPa:order_by_tie
{
I i;
S s;
friend auto as_tie( SiPa const& self ) {
return std::tie(self.i, self.s);
}
};
template<class I = int, class S = int>
struct AKey:order_by_tie
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
friend auto as_tie( AKey const& self ) {
return std::tie(self.keySet, self.keyI);
}
};
then
std::map<AKey<int,int>, bool> myMap;
works.
as_tie uses C++14, because the alternative is annoying. You can add a -> decltype(std::tie( blah, blah )) for C++11 (repeating yourself).
According to http://www.cplusplus.com/reference/set/set/operators/
The other operations also use the operators == and < internally to compare the elements, behaving as if the following equivalent operations were performed:
Notice that none of these operations take into consideration the internal comparison object of neither container.
So the comparaison of std::set<SiPa<I, S>, SiPaComparator<I,S>> is done with
operator < (const SiPa<I, S>&, const SiPa<I, S>&)
and not with
SiPaComparator<I, S>{}
The workaround is to define that operator <.
Given the following function:
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
return false;
return true;
}
how can I determine if the given T overrides the != operator? If someone uses a type without overloading it might end up using a simple binary comparison which might silently lead to wrong results. So I want to ensure only classes that have their own != operator overloading can be used here.
[update 1 - boost 'semi' solution]
I realized that example from my first answer does not work if your class has conversion operator (to type which allows for != comparision), to fix it you can use boost has_not_equal_to type trait. Still its not perfect, as in some cases it generates compilation error instead of giving a value. Those cases are listed in provided link.
[update 2 - concepts solution]
Example with use of concepts:
#include <iostream>
#include <vector>
template<typename T>
concept bool OperatorNotEqual_comparable()
{
return requires (T a, T b) {
{ a.operator!=(b) } -> bool;
};
}
template <OperatorNotEqual_comparable T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
return false;
return true;
}
struct sample1{
bool operator!=(const sample1&) const { return true; }
};
struct sample2{
};
struct sample3{
operator void*() { return 0; }
};
int main() {
// Compiles ok!
std::vector<sample1> vec1;
equals(vec1, vec1);
// Fails, which is OK!
//std::vector<sample2> vec2;
//equals(vec2, vec2);
// Fails, which is OK!
//std::vector<sample2*> vec2;
//equals(vec2, vec2);
// Fails, which is OK!
//std::vector<int> vec4;
//equals(vec4, vec4);
// Fails, which is OK!
//std::vector<sample3> vec5;
//equals(vec5, vec5);
}
http://melpon.org/wandbox/permlink/txliKPeMcStc6FhK
[old answer - SFINAE solution, does not check for conversion operator]
You can use SFINAE, and in the near future concepts (they are in gcc 6.0),
#include<iostream>
#include<string>
#include<type_traits>
template<typename T>
class has_not_equal{
template<typename U>
struct null_value{
static U& value;
};
template<typename U>
static std::true_type test(U*,decltype(null_value<U>::value!=null_value<U>::value)* = 0);
static std::false_type test(void*);
public:
typedef decltype(test(static_cast<T*>(0))) type;
static const bool value = type::value;
};
struct sample1{
bool operator!=(const sample1&) { return true; }
};
struct sample2{
};
int main(){
std::cout<<std::boolalpha;
std::cout<<has_not_equal<int>::value<<std::endl;
std::cout<<has_not_equal<std::string>::value<<std::endl;
std::cout<<has_not_equal<sample1>::value<<std::endl;
std::cout<<has_not_equal<sample2>::value<<std::endl;
}
output:
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
true
true
true
false
live
above code is a modified version from this site, it was for operator==, I changed it to operator!=
If T has no operator !=, then the template wont get instantiated for that type, but you will get a (possibly horribly long and unreadable) error message from your compiler. On the other hand, it T has a operator !=, then it should be just fine to use it. There wont be silent wrong result, unless Ts operator != is anyhow broken.
The only other (besides the conversion) case (I can think of) where a binary comparison can happen (has operator!= defined) and lead to silently wrong results is when T is actually a pointer and you expect a "deep comparison".
One could add an overload for vectors containg pointers but that wouldn't cover pointer to array storage.
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b)
{
return std::equal(a.begin(), a.end(), b.begin());
}
template <typename T>
static bool equals(const std::vector<T *> &a, const std::vector<T *> &b)
{
return std::equal(a.begin(), a.end(), b.begin()
[](T* ap, T* bp) -> bool { return *ap == *bp; });
}
If you are interested in check whether there is an operator != defined in a class (and it has precisely given signature) you may want this approach (tests taken from marcinj's code):
#include <iostream>
#include <vector>
#include <type_traits>
template <class T, class = void>
struct has_not_equal: std::false_type { };
template <class T>
struct has_not_equal<T, typename std::enable_if<std::is_same<decltype(static_cast<bool (T::*)(const T&)const>(&T::operator!=)), bool (T::*)(const T&)const>::value>::type >: std::true_type { };
struct sample1{
bool operator!=(const sample1&) const { return true; }
};
struct sample2{
};
struct sample3:sample2 {
bool operator!=(const sample2& b) const { return true; }
};
struct sample4:sample2 {
bool operator!=(const sample2& b) const { return true; }
bool operator!=(const sample4& b) const { return true; }
};
int main(){
std::cout<<std::boolalpha;
std::cout<<has_not_equal<int>::value<<std::endl;
std::cout<<has_not_equal<std::string>::value<<std::endl;
std::cout<<has_not_equal<sample1>::value<<std::endl;
std::cout<<has_not_equal<sample2>::value<<std::endl;
std::cout<<has_not_equal<sample3>::value<<std::endl;
std::cout<<has_not_equal<sample4>::value<<std::endl;
}
Output of the program:
false
false
true
false
false
true
You may easily add allowed operator != overloads by adding specialization of the has_not_equal struct...
Even though the title of my question here says I'm looking for a way to determine if the != operator is defined, the real question (as you can read from the description and my comment) was how to ensure that I don't silently get wrong results for a type T which has no != operator defined. The existing answers brought up good points, but the real answer is this:
1) You will get a compiler error on the if (a[i] != b[i]) line if you instantiate the template with a value type array using a type that's missing the != operator override (e.g. std::vector<sample2> from marcinj's answer) or you get hard to understand compiler error if you use std::equal instead. In this case the explicit comparison is much more helpful when looking for a solution.
2) If you have a reference type in the vectors to compare you will at first get no problem at all (since there are comparison operators defined for references, even though they only do a flat comparison via the address values). If you want to ensure that the comparison works as if you had used value types (including deep comparison) then add a specialization for vectors with pointer types, as pointed out by Pixelchemist. However, I cannot get the std::equals variants to compile at the moment.
At the end the solution that works for me is this:
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i])
return false;
return true;
}
template <typename T>
static bool equals(const std::vector<T*> &a, const std::vector<T*> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (*a[i] != *b[i])
return false;
return true;
}
Tested with the sample1/2/3/4 structs from marcinj's answer. It's important to note that the operator overloading must use a value type (operator == (const sample1&)) instead of a reference type.
I have still upvoted all answers that gave me useful information to get the final answer.
If someone uses a type without overloading it might end up using a simple binary comparison which might silently lead to wrong results.
There's not such thing as "binary comparison" in C++. Either your class/struct has operator!= or the template won't be instantiated (and if there are no other candidates then it will be an error).
Do anyone know a general method to declare a comparision function for struct so that I can use it in sort , priority queue , map ,set ...
I would also know how to specify the comparision function when declaring a data structure (like map ) having a structure as a key (in the case where i have two or more comparision functions)
Thank you in advance
How can the method be "general"?
Let's say you have this struct.
struct MyStruct{
A a; // A is your own class
};
How would the compiler know how to compare objects of type A?
You need to define a comparison operator yourself.
bool operator()(const MyStruct& s1, const MyStruct& s2);
This function can be given as a compare-function when creating for example a std::map.
explicit map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
std::map
comp: Binary predicate that, taking two element keys as argument, returns true if the first argument goes before the second argument in the strict weak ordering it defines, and false otherwise.
defaults to
less<key_type>
The comparison function depends from the semantics of your struct. What does it mean that a < b for your type?
In general, a compare function is something along the line of this (references are optional):
bool comp( const YourType& a, const YourType& b );
To make a map use your compare function, you must write like this:
#include <map>
struct YourType{
int v;
};
struct YourTypeComparison{
bool operator()( const YourType& a, const YourType& b ) { return a.v < b.v; }
};
int main()
{
std::map<YourType,int, YourTypeComparison> m;
}
Normally you would use the standard containers like std::map< std::string, int >. But they also have a Comparator type and an Allocator type.
The Comparator used by default is std::less, which looks somewhat like this,
template <class T>
struct less : binary_function <T,T,bool> {
bool operator() (const T& x, const T& y) const {
return x<y;
}
};
(There are some other already made functors http://en.cppreference.com/w/cpp/utility/functional)
Notice that it compares two objects with <. This means that as a "general method" you only need to implement the operator bool operator< (const X& lhs, const X& rhs){...} to allow your objects to be sorted. See Operator Overloading FAQ. As a rule of thumb, if you're going to implement one comparison operator then you should implement the others too.
If you need to sort your keys in another way you can define your own comparator (functor).
template < class T >
struct myLess {
bool operator()( const T& lhs, const T& rhs ) const {
return lhs < rhs;
}
};
And use it in a map like std::map< int, int, myLess<int> >.
You can also not use templates at all if you only need to compare one type.
struct myLess {
bool operator()( const int& lhs, const int& rhs ) const {
return lhs < rhs;
}
};
Then you only have to write std::map< int, int, myLess >.
Keep in mind that the objects you're comparing are the Key types, not necessarily the Contained types.
Trying to brush up on my C++ and STL proficiency, running into a problem with std::map keyed by a structure I've defined. Relevant code:
typedef struct key_t {
int a;
int b;
bool operator==(const key_t& rhs)
{
return (a == rhs.a) && (b == rhs.b);
}
bool operator<(const key_t& rhs) //added the when I saw this error, didn't help
{
return a < rhs.a;
}
} key_t;
std::map<key_t, int> fooMap;
void func(void)
{
key_t key;
key.a = 1;
key.b = 2;
fooMap.insert(std::pair<key_t, int>(key, 100));
}
Error looks like this:
"/opt/[redacted]/include/functional", line 133: error: no operator "<" matches these operands
operand types are: const key_t < const key_t
detected during:
instantiation of "bool std::less<_Ty>::operator()(const _Ty &, const _Ty &) const [with _Ty=key_t]" at line 547 of "/opt/[redacted]/include/xtree"
instantiation of "std::_Tree<_Traits>::_Pairib std::_Tree<_Traits>::insert(const std::_Tree<_Traits>::value_type &) [with _Traits=std::_Tmap_traits<key_t, UI32, std::less<key_t>, std::allocator<std::pair<const key_t, UI32>>, false>]"
What am I doing wrong? Is it just flat-out awful/impossible to use structures as a map key? Or something else I'm overlooking?
This
bool operator<(const key_t& rhs)
needs to be a const method
bool operator<(const key_t& rhs) const
The two are different signatures, and std::less looks for the latter. The latter, as a const method, implying that it won't modify the object. The former however without const may imply that a modification to this may be performed.
In general its a good idea to have const methods, even if you can cast in away, it implies a promise to the client that no modifications will take place.
For starters, the operators have to be const. (And you don't need the == operator.)
And where did you learn to use a typedef for a struct. There's no reason for it.
And finally, if you want both elements to participate as part of
the key, you'll have to compare both of them:
struct Key
{
int a;
int b;
bool operator<( Key const& rhs ) const
{
return a < rhs.a
|| ( !(rhs.a < a) && b < rhs.b );
}
};
Otherwise, Key( 1, 2 ) and Key( 1, 3 ) will effectively be
equal.
I'm having an array of structure containing three fields:
struct data{
int s;
int f;
int w;
};
struct data a[n];
In order to sort the array of structure based on field f I'm using my own comparison operator :
bool myf( struct data d1,const struct data d2){
return d1.f < d2.f ;
}
The above operator works fine in inbuilt sort() function :
sort(a,a+n,myf);
but it's not working for upper_bound() function :
upper_bound(a,a+n,someValue,myf);
Can anyone tell me where am I doing wrong ? Is my comparison operator wrong ? If it's wrong, why is it working for sort() function and not upper_bound() ?
I'm getting following on compilation :
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h: In function ‘_FIter std::upper_bound(_FIter, _FIter, const _Tp&, _Compare) [with _FIter = data*, _Tp = int, _Compare = bool (*)(data, data)]’:
prog.cpp:37: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h:2243: error: conversion from ‘const int’ to non-scalar type ‘data’ requested
All you actually need here is to create operator< for your type:
inline bool operator<( const data& lhs, const data& rhs ) {
return lhs.f < rhs.f;
}
and standard algorithms will magically work for you.
In C++ you don't need struct when referring to a type like in C, and you want to pass by const reference to avoid copying.
Edit 0:
The above overloads standard comparison operator < for your type. You would use it implicitly as:
data values[N];
// ... populate
std::sort( values, values + N );
or explicitly with a standard functor:
std::sort( values, values + N, std::less<data>());
Edit 1:
See that compiler tells you _Tp = int in the warning? You need to pass an instance of data as third argument to upper_bound, not int:
data xxx = { 0, 1, 7 };
auto iter = std::upper_bound( values, values + N, xxx );
You can also create overloads for comparing to integers, like:
inline bool operator<( const data& lhs, int rhs ) {
return lhs.f < rhs;
}
inline bool operator<( int lhs, const data& rhs ) {
return lhs < rhs.f;
}
for your original invocation to work.
Primarily, it isn't working because the upper_bound overload that accepts a custom sorting takes four parameters:
// http://en.cppreference.com/w/cpp/algorithm/upper_bound
template< class ForwardIt, class T, class Compare >
ForwardIt upper_bound( ForwardIt first, ForwardIt last, const T& value,
Compare comp );
It was suggested in another answer that you introduce operator< for your type. However, do not do this just for the sake of one specific sorting. Only introduce comparison operators iff they actually make sense for your type.
If you don't follow this rule, future programmers might use your type and wonder about why something works that shouldn't, or vice versa. Your future evil twin may also want to use another sorting for his purpose.
E.g., it makes sense for a complex-datatype class, a SIMD-class (like std::valarray), but it doesn't make any specific sense for example for an Employee class:
Employee foo, bar;
if (bar > foo) {
// is bar taller than foo?
// is bar older than foo?
// is bar working better than foo?
// is bar bigger newbie than foo?
}
Instead, you could do it like this:
namespace employee_ordering {
struct by_name_ascending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() < rhs.name();
}
};
struct by_name_descending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() > rhs.name();
}
}
};
....
upper_bound(first, last, ..., employee_ordering::by_name_ascending());