I'm building a software where one class is responsible to log info sources and commands (both are grouped as requests), where all requests are inserted inside a multimap, wherein the multimap is keyed by the request name, and each element points to request structure that holds management information and callback function pointer, insighted from this software.
The callbacks are executed to issue a command, or to get an info, and everything is ok until here.
To enable subscription-based information delivery, I've introduced a new map keyed by the request iterator, so where calling subscribe("infoID") the software looks for the exact match request and return its iterator.
Because these iterators are unique per request, I've found it useful to key the subscriptions map using it. Where the key points to info subscriber's callback-functions.
The error is:
error: no match for 'operator<' (operand types are 'const
std::__detail::_Node_iterator<std::pair<const
std::__cxx11::basic_string, request>, false, true>' and 'const
std::__detail::_Node_iterator<std::pair<const
std::__cxx11::basic_string, request>, false, true>')
{ return __x < __y; }
Followed by 15 compiling notes 'template argument deduction/substitution failed':
'const std::__detail::_Node_iterator<std::pair<const
std::__cxx11::basic_string, request>, false, true>' is not
derived from 'const std::pair<_T1, _T2>'
{ return __x < __y; }
each one with a unique source: const std::pair<_T1, _T2>, const std::reverse_iterator<_Iterator> (stl_function.h), const std::reverse_iterator<_Iterator> (stl_iterator.h), ... etc.
Full error here.
Code:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
#include <functional>
#include <map>
using namespace std;
struct request
{
string f1;
};
using SYS_REQMAP =unordered_multimap<string, request, hash<string>>;
using SYS_REQMAP_I =SYS_REQMAP::iterator;
using SYS_INFOSUB_CBF = function<void(string, string)>;
using SYS_INFOSUB_CBFS = vector<SYS_INFOSUB_CBF>;
using SYS_REQINF_SUBS = map<SYS_REQMAP_I, SYS_INFOSUB_CBFS>;
void cbf(const string& a, const string& b){}
int main()
{
SYS_REQINF_SUBS infoSubr;
SYS_REQMAP vm{{"cmd1", {"foo"}},
{"cmd2", {"bar"}}};
for (SYS_REQMAP_I it = vm.begin(); it != vm.end(); it++)
{
infoSubr[it].push_back(cbf); // Compile error
}
}
void compilesOK()
{
using SYS_REQINF_SUBS_1 = std::map<int, SYS_INFOSUB_CBFS>;
SYS_REQINF_SUBS_1 subs1;
subs1[1].push_back(cbf); // Compiles OK
}
And here's OnlineGDB link to compile and observe output.
The key part of the error is:
stl_map.h:481:32: required from 'std::map<_Key, _Tp, _Compare, ....>
[with _Key = std::__detail::_Node_iterator ....
...
stl_function.h:386:20: error: no match for 'operator<'
std::map (aka ordered map) keys are required to be comparable (operator<), but map iterators are not comparable, and thus cannot be used in an ordered map.
The simpliest solutions is to use some other type.
Other solutions are to provide the map with a comparitor (_Compare), to tell it how to compare iterators, or switch to a unordered_map and provide a hasher to tell it how to hash iterators.
The iterator requires a custom comparison function _Compare:
struct Compare_REQMAP_I
{
bool operator()(const SYS_REQMAP_I& lhs, const SYS_REQMAP_I& rhs) const {
return &lhs < &rhs;
}
};
using SYS_REQINF_SUBS = std::map<SYS_REQMAP_I, SYS_INFOSUB_CBFS, Compare_REQMAP_I>;
Related
I've tried to narrow down my problem to a minimal example:
#include <algorithm>
#include <map>
#include <string>
#include <vector>
int main()
{
std::vector<int> result;
std::map<std::string, std::pair<unsigned int, std::vector<int>>> other;
if (true)
{
std::for_each(other.begin(), other.end(),
[&](std::pair<std::string, std::pair<unsigned int, std::vector<int>>> & data)
{
result.insert(result.end(), data.second.second.begin(), data.second.second.end());
});
}
return 0;
}
I get a compiler error:
error C2664: 'void main::<lambda_1b93236899a42921c1aec8d5288e5b90>::operator ()(std::pair<std::string,std::pair<unsigned int,std::vector<int,std::allocator<_Ty>>>> &) const': cannot convert argument 1 from 'std::pair<const _Kty,_Ty>' to 'std::pair<std::string,std::pair<unsigned int,std::vector<int,std::allocator<_Ty>>>> &'
As far as I can tell the lambda parameter is indeed a reference to the type that is contained by the map which we are iterating through. That is the type I am supposed to be using, right?
If I remove the amperstand before data it compiles.
Why?
I do not want to pass each element by value, as those collections will contain a lot of data in my real program.
If I replace the lambda param with auto & it compiles, which leads me to believe the type in the lambda param does not match the type contained by the map, but it sure looks like it does to me. Also, why would the original compile without & if the type is wrong?
What am I missing/not understanding?
std::map<Key, T>'s value_type is std::pair<const Key, T>, not std::pair<Key, T>. The version without an ampersand makes a copy of each pair. Since you can copy const Key to Key everything is fine. Add a const to your lambda's parameter type.
#include <algorithm>
#include <map>
#include <string>
#include <vector>
int main()
{
std::vector<int> result;
std::map<std::string, std::pair<unsigned int, std::vector<int>>> other;
if (true)
{
std::for_each(other.begin(), other.end(),
[&](std::pair<const std::string, std::pair<unsigned int, std::vector<int>>> & data)
// Add this const ^^^^^
{
result.insert(result.end(), data.second.second.begin(), data.second.second.end());
});
}
return 0;
}
I've been strugglig with the very same problem today.
I solved that making the key value type in the std::pair const:
[&](std::pair<const std::string, std::pair<unsigned int, std::vector<int>>> & data)
// ^^^^^
After thinking about that a bit, it's quite logical:
You cannot change the key_value of a std::map's entry to something different. So if it's working with an auto loop and a reference it needs to be specified as const.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm working on a legacy code which is poorly written and only compiles within Microsoft Visual Studios and with Visual C++ compiler. GCC, G++, or Clang all fails to compile the code due to build time errors. I have narrowed down the issue to the following class declaration which instantiate a STL container of the class type within the class declaration:
#include <map>
#include <set>
#include <iomanip>
#include <string>
#include <cmath>
#include <iterator>
#include <unordered_map>
#include <bits/unique_ptr.h>
#define HASH_MAP unordered_map
using namespace std;
namespace XYZ {
class abc {
public:
typedef HASH_MAP<double, abc> MAP; // This is the problem ?
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
typedef pair<double, abc> Pair;
bool less(abc::Pair& a, abc::Pair& b) { return a.first < b.first; }
public:
abc() {}
~abc() { }
};
}
I want to know what is the best way to refactor this code segment while preserving the structure of the code. For example, I was trying to make the MAP definition with a pointer type (i.e., typedef HASH_MAP<double, XYZ*> MAP) This change worked with GCC compiler however, since, I'm changing to pointer type I would have to dig deep in to the code base and modify most of the code base as this class is playing a key role in other dependent code.
So I was wondering if there is an alternative to fix this issue which would not require significant change to the original code base. I was thinking in the line of making a friend class similar.
Following is the compiler error:
In file included from /usr/include/c++/4.8/bits/stl_algobase.h:64:0,
from /usr/include/c++/4.8/bits/stl_tree.h:61,
from /usr/include/c++/4.8/map:60,
from /home/user/work/wxy.h:4,
from /home/user/work/abc.h:4,
from /home/user/work/abc.cpp:1:
/usr/include/c++/4.8/bits/stl_pair.h: In instantiation of ‘struct std::pair<const double, XYZ::abc>’:
/usr/include/c++/4.8/type_traits:615:28: required from ‘struct std::__is_destructible_impl<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:637:12: required from ‘struct std::__is_destructible_safe<std::pair<const double, XYZ::abc>, false, false>’
/usr/include/c++/4.8/type_traits:652:12: required from ‘struct std::is_destructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:116:12: required from ‘struct std::__and_<std::is_destructible<std::pair<const double, XYZ::abc> >, std::__is_direct_constructible_impl<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&> >’
/usr/include/c++/4.8/type_traits:817:12: required from ‘struct std::__is_direct_constructible_new_safe<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&>’
/usr/include/c++/4.8/type_traits:895:12: [ skipping 4 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/4.8/type_traits:968:12: required from ‘struct std::__is_copy_constructible_impl<std::pair<const double, XYZ::abc>, false>’
/usr/include/c++/4.8/type_traits:974:12: required from ‘struct std::is_copy_constructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/bits/alloc_traits.h:540:12: required from ‘struct std::__is_copy_insertable<std::allocator<std::pair<const double, XYZ::abc> > >’
/usr/include/c++/4.8/bits/alloc_traits.h:560:63: required by substitution of ‘template<class _Alloc> using __check_copy_constructible = std::__allow_copy_cons<std::__is_copy_insertable<_Alloc>::value> [with _Alloc = std::allocator<std::pair<const double, XYZ::abc> >]’
/usr/include/c++/4.8/bits/unordered_map.h:97:11: required from ‘class std::unordered_map<double, XYZ::abc>’
/home/user/work/abc.h:27:20: required from here
/usr/include/c++/4.8/bits/stl_pair.h:102:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
_T2 second; /// #c second is a copy of the second object
^
In file included from /home/user/work/abc.cpp:1:0:
/home/user/work/abc.h:24:11: error: forward declaration of ‘class XYZ::abc’
class abc {
^
The issue is (for whatever reasons -- hell, it's just a typedef!) with the Iterators. If you move those out of your class code, so that the class definition is complete at the point of their definition, it compiles (with g++). Perhaps one then should rename Iterator to MAP_Iterator or such. I can imagine that the changes required in client code then are manageable
Edit: After your comment I had the idea to put the iterator typedefs in a class called abc (the original class name) to retain source compatibility with the client code. The actual class definition is moved to a base class which the client code doesn't need to use explicitly. The map holds objects of the base class, which involves slicing and back conversion when storing or retrieving true abcs. It would probably not be possible to hold abc references to values in the map, but the simple value-based example below (using the iterators) works. There are a few comments in the source code.
#include <unordered_map>
#include <iostream>
using namespace std;
// The "original" abc
class abcBase
{
public:
typedef unordered_map<double, abcBase> MAP; // This is the problem ?
typedef pair<double, abcBase> Pair;
bool less(abcBase::Pair& a, abcBase::Pair& b) { return a.first < b.first; }
string tag;
public:
abcBase(string t): tag(t){}
abcBase() = default;
~abcBase() { }
};
// The abc presented to the users for source compatibility.
// There is a conversion
// from base to derived via constructor.
//
// Note that a MAP
// holds abcBase objects, not abc objects! We need to convert them
// when we store and when we read.
// Conversion derived -> base is via slicing
// (which does not do any harm as long as we do not define
// data members in derived).
class abc: public abcBase
{
public:
// conversion constructor
abc(const abcBase &b): abcBase(b){}
abc(string t): abcBase(t){}
abc() = default;
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
};
int main()
{
abc::MAP m;
abc a("a"),b("b"),c("c");
m[1.0] = a; // conversion abc -> abcBase ...
m[2.0] = b;
m[3.0] = c;
a = m[1.0]; // conversion abcBase -> abc via ctor
for( abc::Iterator i = m.begin(); i != m.end(); ++i)
{
cout << "key: " << i->first << ", val: " << i->second.tag << endl;
}
return 0;
}
You're using std::unordered_map so you're using C++11.
So I suppose that the use of std::unique_ptr (typedef HASH_MAP<double, std::unique_ptr<XYZ>> MAP instead of typedef HASH_MAP<double, XYZ*> MAP) can help you a lot in reducing the following modifications.
By example, the following code compile
#include <utility>
#include <memory>
#include <unordered_map>
using namespace std;
#define HASH_MAP unordered_map
namespace abc {
class XYZ {
private:
int _test;
public:
typedef HASH_MAP<double, std::unique_ptr<XYZ>> MAP; // This is the problem ?
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
typedef pair<double, std::unique_ptr<XYZ>> Pair;
bool less(XYZ::Pair& a, XYZ::Pair& b) { return a.first < b.first; }
public:
XYZ():_test(0) {}
~XYZ() { }
// Some function definitions
};
};
int main ()
{
return 0;
}
Obviously I'm supposing your namespace is abc and your class name is XYZ; you can't put a namespace in a map.
p.s.: sorry for my bad English.
Consider the following code:
#include <string>
#include <map>
#include <memory>
#include <utility>
#include <iostream>
typedef std::shared_ptr<const std::string> ConstDataTypePtr;
typedef std::map<std::string, ConstDataTypePtr> StrDataTypeMap;
int main()
{
StrDataTypeMap m_nameToType;
ConstDataTypePtr vp_int8(new std::string("RGH"));
m_nameToType.insert(std::make_pair<std::string, ConstDataTypePtr>("int8_t", vp_int8));
return 0;
}
You must compile it with: g++ -std=c++11 <filename>.cpp.
It gives the following error:
testO.cpp: In function ‘int main()’:
testO.cpp:14:88: error: no matching function for call to ‘make_pair(const char [7], ConstDataTypePtr&)’
m_nameToType.insert(std::make_pair<std::string, ConstDataTypePtr>("int8_t", vp_int8));
^
testO.cpp:14:88: note: candidate is:
In file included from /usr/include/c++/4.8.2/bits/stl_algobase.h:64:0,
from /usr/include/c++/4.8.2/bits/char_traits.h:39,
from /usr/include/c++/4.8.2/string:40,
from testO.cpp:1:
/usr/include/c++/4.8.2/bits/stl_pair.h:276:5: note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
make_pair(_T1&& __x, _T2&& __y)
^
/usr/include/c++/4.8.2/bits/stl_pair.h:276:5: note: template argument deduction/substitution failed:
testO.cpp:14:88: note: cannot convert ‘vp_int8’ (type ‘ConstDataTypePtr {aka std::shared_ptr<const std::basic_string<char> >}’) to type ‘std::shared_ptr<const std::basic_string<char> >&&’
m_nameToType.insert(std::make_pair<std::string, ConstDataTypePtr>("int8_t", vp_int8));
From what I am reading of the error, the compiler is expecting an r-value when I am trying to insert into the map. Why? What mistake have I made here?
Kindly note that I created this snippet from some existing code which is part of a large code-base. It is probably also worth mentioning that the snippet has been taken from a code base which was run on Windows and I have the task of porting it to Linux. The original author had used std::tr1::shared_ptr. I modified it to use std::shared_ptr. I didn't expect any repercussions because of this change.
The whole point of std::make_pair is to let compiler deduce types. If you want to provide type, use std::pair<K, V>
So
m_nameToType.insert(std::make_pair<std::string, std::string>("int8_t", vp_int8));
should be:
m_nameToType.insert(std::make_pair("int8_t", vp_int8));
or
m_nameToType.insert(std::pair<const std::string, ConstDataTypePtr>("int8_t", vp_int8));
or simply:
m_nameToType.emplace("int8_t", vp_int8);
#include <memory>
#include <map>
#include <string>
int main() {
using shared_data = std::shared_ptr<const std::string>;
std::map<std::string, shared_data> map;
map.insert(std::make_pair(
"something",
shared_data(new std::string("something else"))
));
return 0;
}
see: http://ideone.com/4AQfqd
Back to your problem;
testO.cpp:14:83: note: cannot convert ‘vp_int8’ (type ‘ConstDataTypePtr {aka std::shared_ptr >}’) to type ‘std::basic_string&&’
m_nameToType.insert(std::make_pair("int8_t", vp_int8));
What you have:
std::make_pair<std::string, std::string>(some_string, TOTALLY_NOT_A_STRING)
You gave wrong types to the std::make_pair template. Just change
m_nameToType.insert(std::make_pair<std::string, std::string>("int8_t", vp_int8));
Into
m_nameToType.insert(std::make_pair<std::string, ConstDataTypePtr>(std::string("int8_t"), vp_int8));
(note the std::make_pair<std::string, ConstDataTypePtr> part)
EDIT: or don't provide template params at all, as someone suggested in comment.
Don't mention the types in the template in make_pair function.
m_nameToType.insert(std::make_pair("int8_t", vp_int8));
I have the following piece of code, which saves structs into a boost::ptr_vector container. I am trying now to write a simple search function for this container via equal_range. I chose that function because I want a pointer to the element of the sequence (if it is found), or pointers to the lower and upper bound (if the element is not found):
struct COMP
{
bool operator()(const merkle_tree_node &LHS, const std::string& query){
return (LHS.word < query);
}
};
std::pair<boost::ptr_vector<merkle_tree_node>::iterator,
boost::ptr_vector<merkle_tree_node>::iterator>
search_tree(merkle_tree vWords, std::basic_string<char> query, size_t length)
{
return std::equal_range(vWords.begin(), vWords.begin()+(length-1),
query,
COMP());
}
Which I am calling via my main function as such:
std::basic_string<char> QUERY = "SOMETHING";
std::pair<boost::ptr_vector<merkle_tree_node>::iterator,
boost::ptr_vector<merkle_tree_node>::iterator> result =
search_tree(vWords, QUERY, vWords.size());
However, I am getting the following compilation error which I just can't seem to overcome:
In file included from /usr/include/c++/4.8/algorithm:62:0,
from vf-merkle.cpp:3:
/usr/include/c++/4.8/bits/stl_algo.h: In instantiation of ‘std::pair<_FIter, _FIter> std::equal_range(_FIter, _FIter, const _Tp&, _Compare) [with _FIter = boost::void_ptr_iterator<__gnu_cxx::__normal_iterator<void**, std::vector<void*, std::allocator<void*> > >, merkle_tree_node>; _Tp = std::basic_string<char>; _Compare = COMP]’:
vf-merkle.cpp:111:10: required from here
/usr/include/c++/4.8/bits/stl_algo.h:2668:36: error: no match for call to ‘(COMP) (const std::basic_string<char>&, merkle_tree_node&)’
else if (__comp(__val, *__middle))
^
vf-merkle.cpp:98:8: note: candidate is:
struct COMP
^
vf-merkle.cpp:100:7: note: bool COMP::operator()(const merkle_tree_node&, const string&)
bool operator()(const merkle_tree_node &LHS, const std::string& query){
^
vf-merkle.cpp:100:7: note: no known conversion for argument 1 from ‘const std::basic_string<char>’ to ‘const merkle_tree_node&’
Any ideas?
The short answer, is you need to provide both overloads for different orderings of the arguments
struct COMP
{
bool operator()(const merkle_tree_node &LHS, const std::string& query){
return (LHS.word < query);
}
bool operator()(const std::string& query,const merkle_tree_node &RHS){
return (query < RHS.word);
}
};
You need to do this because you are calling std::equal_range with the third argument of type string, while the iterators point to merkle_tree_node. This mixed comparison case requires you to provide additional overloads to handle the case where the string is the first argument, or when the string is the second argument. For completeness, you might want to consider adding the case where it's two instances of merkle_tree_node.
You call algorithm std:;equal_range passing to it std:;string as the third argument instead of an iterator, So your using of the algorithm is invalid. Read the description of std::equal_range before using it.
I've got an error while using find() function. Here is the code:
#include <iostream>
#include <map>
#define N 100000
using namespace std;
int main (int argc, char * const argv[]) {
map<int,int> m;
for (int i=0; i<N; i++) m[i]=i;
find(m.begin(), m.end(), 5);
return 0;
}
I'm getting an compiller error:
error: no match for 'operator==' in '__first. __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::operator* [with _Iterator = std::_Rb_tree_iterator<std::pair<const int, int> >, _Sequence = __gnu_debug_def::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >]() == __val'
Including 'algorithm' nothing changes. Compiling in VS2008 shows similar error.
I know about m.find(), but I realy need to use find() too.
Thanks a lot for your assistance!
P.S. Actualy, the task is to compare speed of m.find(5) and find(m.begin(), m.end(), 5), so I need to make both of them work properly.
begin() and end() on all STL containers provide access to elements of those collections. Type of those elements is known as value_type of the container. For std::map<Key, Value>, its value_type is std::pair<Key, Value>. Therefore, your find function is trying to find a pair<int, int> which is equal to 5. Since there's no operator== defined to compare pair<int, int> and int, you get the error.
The correct way to do this (so long as you want to avoid member find()) is to use std::find_if:
template <class First>
struct first_equal
{
const First value;
first_equal(const First& value)
: value(value)
{
}
template <class Second>
bool operator() (const std::pair<First, Second>& pair) const
{
return pair.first == value;
}
};
...
find_if(m.begin(), m.end(), first_equal<int>(5));
You could also overload operator== for pair and int to do what you want, but it's a very hackish way (because it will affect all your code, and because such a comparison has no meaning in general).
find() requires a parameter that can be compared to *iterator. For your map, this will be pair<int,int>. You'll need to create a dummy pair, plus a comparison functor to compare the pairs.
Just use m.find(5)