Can't push a map inside a vector? - c++

The following code doesn't work, it gives the following errors:
No matching function for call to object of type 'const comparer'
and
Call to object of type 'value_compare' (aka 'std::_1::_map_value_compare, int, comparer, true>') is ambiguous
Here is the code:
struct comparer
{
bool operator()(const std::string x, const std::string y)
{
return x.compare(y)<0;
}
};
int main(int argc, char **argv)
{
vector< map<string,int,comparer> > valMapVect;
map<string,int,comparer> valMap;
valMapVect.push_back(valMap);
}
It is compiled with Xcode 5.x (so on a Mac).
Somebody has an idea of what is wrong? I think it was working a while ago when I was compiling it on Linux. Is it possible?

It seems libc++ wants the function call operator in comparer to be a const member function:
struct comparer
{
bool operator()(const std::string x, const std::string y) const
{ // ^^^^^ fixes the problem
return x.compare(y)<0;
}
};
Personally I would pass the arguments as std::string const& (note the &) but that doesn't change whether libc++ likes the comparison object. I'm not, yet, sure if the standard mandates that the const is present. I didn't spot such a requirement which would imply that the comparison function would have to be kept as a mutable member. However, given that it is often stateless, it is desirable to derive from it to not waste any memory (i.e., take advantage of the empty base optimization) which is probably what libc++ does. It isn't quite clear whether
it is a bug in libc++, i.e., that the comparison function object has to be stored a mutable member.
in the standard to not mandate the function call operator to be const.
in the code using it to make the function call operator const.
The easiest fix is, however, to make function call operator const.

Related

Why does std::sort fail to find the appropriate (static member) function overload? [duplicate]

This question already has answers here:
How do I specify a pointer to an overloaded function?
(6 answers)
Closed 4 years ago.
I have a class which offers custom static comparators which can be used by std::sort. The following would compile just fine (stripped down to a minimal code example):
#include <vector>
#include <string>
#include <algorithm>
class StringUtils
{
public:
static bool customStringCompare(const std::string&, const std::string&) { return true; }
};
void test()
{
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(), StringUtils::customStringCompare);
}
Now, when I add an overload to the StringUtils class like
static bool customStringCompare(const char*, const char*) { return true; }
the following would work:
void test2()
{
std::string s1, s2;
StringUtils::customStringCompare(s1, s2);
}
However, the std::sort call above produces compiler error C2672 (No matching overload found), C2780 (expected 2 arguments - 3 supported), C2783 (template argument for "_Pr" could not be deduced) in MSVC 2015 Update 2.
Why does std::sort fail to find the matching overload in this case?
In your code std::sort takes a function pointer. How then a compiler can decide which function you want? IntelliSense shows the following error:
cannot determine which instance of overloaded function StringUtils::customStringCompare is intended
To use overloading, you can turn a comparator into a function object:
struct Comparator {
bool operator()(const std::string&, const std::string&) const {
return true;
}
bool operator()(const char*, const char*) const {
return true;
}
};
void test() {
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(), Comparator{});
}
Alternatively, since C++14 you can use a generic lambda function:
void test() {
std::vector<std::string> testList;
std::sort(testList.begin(), testList.end(),
[](const auto& s1, const auto& s2) {
return StringUtils::customStringCompare(s1, s2);
});
}
The issue is that there are two overloads and passing one to std::sort does not clarify which overload should be used1. The compiler cannot deduce this from the usage in the std::sort call. This makes sense: The type of the comparator argument of std::sort is simply a template argument, i.e. it’s completely unspecified: any overload works just as well as any other.
There are multiple workarounds and in practice I generally recommend passing a functor as shown in Evg’s answer.
But it’s important to understand that the error is simply caused by a type that cannot be deduced automatically. So, to make the code compile, it’s sufficient to specify the type explicitly; this will select a single overload:
std::sort(
testList.begin(),
testList.end(),
static_cast<bool (*)(std::string const&, std::string const&)>(StringUtils::customStringCompare)
);
Here we use static_cast to explicitly signal the type of the function (pointer) for the purpose of overload resolution.
1 And, frankly, every single mainstream compiler produces a rotten error message. This has been known for a long time and is entirely fixable. clang++ is slightly better than GCC and MSVC but honestly not by much. But even C#, a completely unrelated language, gives highly idiosyncratic errors in similar situations.

const objects as a function parameter

Consider the following code snippet:
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
};
void print (const MyClass& arg) {
cout << arg.get() << '\n';
}
int main() {
MyClass foo (10);
print(foo);
return 0;
}
Whether I add a const modifier before the instatiatation of MyClass or not, the program successfully compiles (without any warning) and prints 10. Why can print accept a non-const argument? Or, in other words, what is the function of the const modifier in the function parameter? Why can the formal and actual parameters of a function have different types (or modifiers)?
I have tried both GCC (4.8.2) and Clang (3.4) with -Wall -std=c++11 on Ubuntu 14.04, and the results were the same (no errors/warnings). I have also searched "c++ const object function" but didn't get anything that looked promising.
This is completely sane and normal. The object is treated as const within your function; it does not matter that it was not originally created to be immutable.
Of course, the opposite is not true!
void foo(T& rarr);
int main()
{
const T lolwut;
foo(lolwut); // oops
}
const forbids the body of the function from modifying the parameter variable. Both ways compile because you didn't attempt to modify it.
You can overload const and non-const reference parameters, and the const overload will only be chosen if the argument is really const (or a type conversion results in a temporary being passed). (For non-reference parameters, const seldom makes sense and such overloads may not even be defined.)
All that const does in this case is to prevent modification of parameter variable (and in the case of classes, prevent the calling of functions that are not labelled as const). MyClass may be trivially cast to const MyClass because there should be nothing that you can do to a const MyClass that you can't do to a non-const one. The reverse is not true, of course.
(I say "should" above, because it is of course possible to completely subvert const semantics under C++ if you wanted to, so the presence of const in a function prototype is really only a hopeful hint rather than a cast-iron compiler-enforced guarantee. But no sensible programmer should be breaking things like that!)

Usage of const directive in C++ in general, in particular in Tiny XML Library:

I'm an experienced developer in a few compiled OOP languages, particularly Object Pascal and C#. Have 'messed around' with C++ for years but recently started getting more serious about C++ development.
Most of concepts in C++ are quite easy for me to grasp based on my experience with other languages. But one thing that I'm finding quite difficult is the ways in which the const directive is used and how it behaves in C++.
In particular, at the moment I'm faced with this problem, using the TinyXML Library in Netbeans C++ IDE, on an Ubuntu 12.04 machine and default mingW/G++ tool chain:
I'm calling this function:
TiXmlNode::TiXmlNode* FirstChild()
In the TinyXML source there are two overloaded public versions of this function in class TiXmlNode:
const TiXmlNode* FirstChild() const { return firstChild; }
TiXmlNode* FirstChild() { return firstChild; }
They are identical except for the const directive. I assumed that the version called would be dependent on how I declared the variable I was loading from the function, for example:
const TiXmlNode* aNode = node->FirstChild();
Would call the const version of the function
TiXmlNode* aNode = node->FirstChild();
Would call the second version, without const.
But when I try to use the second form in my code, I get a compiler error:
error: invalid conversion from ‘const TiXmlNode*’ to ‘TiXmlNode*’
[-fpermissive]
Why is this happening? How do I use the version of the function without const? What am I missing here?
More - where can I find a good summary of the usages of const directive in C++ 11.
Here, the overload chosen depends on the type of node, not the type of aNode.
const at the end of the member function declaration indicates that the function may be called on a const instance of the object; a member function lacking const there cannot be. When both const and non-const overloads are present, overload resolution prefers the const version when applicable.
Apparently, node is of type const TiXmlNode*, so FirstChild() const overload is chosen, and the return value is of type const TiXmlNode*. This cannot be converted to TiXmlNode*.
Here's a textbook example. std::vector<T> provides two overloads of operator[], looking like this (greatly simplified):
template <typename T>
class vector {
T* storage_;
public:
T& operator[](size_t index) { return storage_[index]; }
const T& operator[](size_t index) const { return storage_[index]; }
};
With this, you could write:
vector<int> v;
v[0] = 42; // OK: calls int& operator[](size_t)
const vector<int> cv;
cv[0] = 42; // Error: calls const int& operator[](size_t) const
As expected, const vector can't be modified, while non-const one can be.

const function and references

If you notice in the following functions they both have the same for loop that searches for a integer location. Pop() compiles but I get an error for top() having to do with the const qualifiers. The heap class inherits from eecs281heap which stores a functor Comp compare where Comp is the typename. The instructor told us the only way to access the functor is through this->() so i'm just lookin for some guidance here. Thanks
error: passing ‘const larger’ as ‘this’ argument of ‘bool larger::operator()(int, int)’ discards qualifiers
This happens after running the following in int main. Through testing I already know the constructor works properly.
vector <int> data={10,2,13};
poorman_heap<int,larger> y(data.begin(),data.end());
template<typename TYPE, typename COMP>
void poorman_heap<TYPE, COMP>::pop() {
int location=0;
for(int i=1;i<data.size();i++){
if(this->compare(data.at(i),data.at(location))){
location=i;
}
}
data.erase(data.begin()+location);
return;
}
template<typename TYPE, typename COMP>
const TYPE& poorman_heap<TYPE, COMP>::top() const {
int location=0;
for(int i=1;i<data.size();i++){
if(this->compare(data.at(i),data.at(location))){
location=i;
}
}
return data.at(location);
}
P.S. greater is
struct greater{
bool operator()(int x,int y){
return x>y;
}
}
Make the call operator of greater a const operator:
struct greater
{
bool operator()(int x,int y) const
{
return x>y;
}
}
The same applies to whatever this->compare resolves to. It needs to be const.
It doesn't make much sense for a comparator to be non-const.
It looks like the problem is that the compare member has operator() declared as a non-const function. Since it sounds like you don't have the ability to change that, you might be able to get the behavior that you want by declaring it as a mutable member in poorman_heap.
The mutable keyword lets you distinguish between an object being "physically const" (meaning the actual bytes don't change and being "logically const" (meaning the bytes might change but the value of the object isn't different in a fundamental sense). Basically it means that something "doesn't count" for the purposes of const-ness. The classic example in my mind is lazy initialization - you want to declare the get_value() function on a class const, but you also don't want to waste time computing the value if no one uses it, so you declare the value mutable and now you're allowed to calculate it and assign to it inside get_value() even though it is a const member function.

unordered_map with referenced key

I have something wrong with the new C++ unordered_map: I would like to use the operator[] with a const key, but I get rejected.
I cannot give the whole code, but I can simplify my problem like this:
#include <unordered_map>
class A {
public:
A();
};
class B {
public:
B();
};
int main(int argc, char **argv) {
std::unordered_map<A &, B> myMap;
A a;
const A &ar = a;
B b;
myMap[ar] = b;
}
The output of the compiler is a bit long, but ends with:
/usr/include/c++/4.6/bits/hashtable_policy.h:537:5: note: no known conversion for argument 1 from ‘const A’ to ‘A&’
I use a const A & because, in my code, some method give it to me as is. And, by the way, the key should be const. I have tried a std::unordered_map<const A &, B> myMap; instead, but it does not work either.
I use gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), with the -std=c++0x flag.
Could you please tell me why this is forbidden? I must say I do not understand the reason.
Many thanks (and please excuse me if the question is stupid...).
The reason is that operator[] is specified as follows (note that the same holds for just std::map):
Value& operator[](Key const& k);
In your case, Key is A&, so this expands to:
B& operator[](A& const& k);
And since references-to-references are invalid and the top-level reference is dropped when created through typedefs or template parameters, you get just:
B& operator[](A&);
Which can't handle an A const& argument.
In general, I'd advise against using a mutable reference as the key, since mutable keys are a good source for errors.
Using a reference as a key is a Bad Idea: It is bound to cause problems because the life-time of the map and the keys aren't aligned. The key type of your map of references is T& (well, there is a const added at the end but it would be illegal). Trying to bind a T const& to a T& doesn't work. Hence, you can't use T const& to look-up in a map using T& as a key.
There are other things which won't work. You should not try to use a map with a key of T& (or T* for that matter): use values as keys!