error in compilation while looping over map - c++

got compilation error while trying to compile simple cpp program
i have tried the following compilation lines
1) gcc hello.cpp -std=gnu++11
2) g++ hello.cpp -std=gnu++11
this is my cpp file
#include <iostream>
#include <map>
#include <string>
using namespace std;
static map<string,int> create_map()
{
map<string,int> mymap = {
{ "alpha", 10 },
{ "beta", 20 },
{ "gamma", 30 } };
return mymap;
}
map<string,int> m= create_map();
int main()
{
cout << "Hello, World!";
for (map<string,int> x: m)
cout << x.first << ": " << x.second << '\n';
return 0;
}
the output of gcc contain a lot of linkage error like
hello.cpp:(.text+0x135): undefined reference to `std::cout'
hello.cpp:(.text+0x13a): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
and g++ output was shorter
hello.cpp: In function 'int main()':
hello.cpp:19:29: error: conversion from 'std::pair<const std::basic_string<char>, int>' to non-scalar type 'std::map<std::basic_string<char>, int>' requested
hello.cpp:20:18: error: 'class std::map<std::basic_string<char>, int>' has no member named 'first'
hello.cpp:20:37: error: 'class std::map<std::basic_string<char>, int>' has no member named 'second'
the code is correct for 99.99%. i don't know really how to compile it properly

There are some very different kind of errors you see.
gcc hello.cpp -std=gnu++11
This tries to compile the code using the GNU c-compiler.
C++ source files are recognized and compiled regarding the c++ syntax, but the linker uses c standard libraries, and the c++ standard libraries aren't automatically linked.
There must be some difference with your source, since I can't reproduce the linker errors you're claiming with this test.
In the other case
g++ hello.cpp -std=gnu++11
your code is compiled using the c++ compiler, and
for (map<string,int> x: m)
is wrong regarding the type for x in the range based loop (which actually is pair<const string,int>).
You should simply change that to
for (const auto& x: m)
to get the code working.
x will be the type of the container element type, that is iterated over from m.
Here's a working online demo.

Replace the loop with:
for (auto const& x: m)
cout << x.first << ": " << x.second << '\n';
Alternatively, you could use:
for (std::pair<string, int> x: m)
or
for (const std::pair<string, int>& x: m)
const because you do not want to change x. & to prevent a copy of the pair into x.
In the range based loop for an std::map, the variable that iterates over the container needs to be std::pair<const Key, T>. This is defined by the value_type for the container's Member types.

Since the type of x is std::pair<const string,int> you need to use instead (after comments):
for (const std::pair<const string,int>& x: m)
const & is added so that redundant pair copies are avoided (in the newest compilers they are probably avoided any way).
for (const auto& x: m)
may alternatively be used!

Related

Trouble definining hash function for custom type in unordered_multimap on MacOS

So I want to use a custom type (here, SWrapper) as the key type for an unordered_multimap. I've defined a hash class, derived from the standard hashing function for strings, and included the hashing class in the type of the multimap. Some code that reproduces the error is displayed below. This compiles on Arch Linux with both g++ and clang++, but on MacOS with clang++, I get errors:
#include <unordered_map>
#include <functional>
#include <string>
class SWrapper {
public:
SWrapper() {
(*this).name = "";
}
SWrapper(std::string name) {
(*this).name = name;
}
bool operator==(SWrapper const& other) {
return (*this).name == other.name;
}
std::string name;
};
class SWrapperHasher {
size_t operator()(SWrapper const& sw) const {
return std::hash<std::string>()(sw.name);
}
};
int main(int argc, char* argv[]) {
auto mm = std::unordered_multimap<SWrapper, int, SWrapperHasher>();
return 0;
}
Running g++ -std=c++11 -Wall -Wpedantic -Wextra hash_map_test.cpp -o hash_map_test on Arch Linux (or clang++) compiles the code with no errors. However, on MacOS, using the same command, I get the following error message:
In file included from hash_map_test.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/unordered_map:408:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__hash_table:868:5: error:
static_assert failed due to requirement 'integral_constant<bool, false>::value' "the
specified hash does not meet the Hash requirements"
static_assert(__check_hash_requirements<_Key, _Hash>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__hash_table:883:1: note: in
instantiation of template class
'std::__1::__enforce_unordered_container_requirements<SWrapper, SWrapperHasher,
std::__1::equal_to<SWrapper> >' requested here
typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/unordered_map:1682:26: note: while
substituting explicitly-specified template arguments into function template
'__diagnose_unordered_container_requirements'
static_assert(sizeof(__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(...
^
hash_map_test.cpp:29:15: note: in instantiation of template class
'std::__1::unordered_multimap<SWrapper, int, SWrapperHasher, std::__1::equal_to<SWrapper>,
std::__1::allocator<std::__1::pair<const SWrapper, int> > >' requested here
auto mm = std::unordered_multimap<SWrapper, int, SWrapperHasher>();
^
1 error generated.
I've tried interpreting the error message, but I'm really at a loss of what to make of it. If anyone has any suggestions as to what's going on here, and how I can work around this on MacOS, it'd be greatly appreciated!
The compiler's complaint unfortunately does not say which requirement was not met. In this case, the problem comes from SWrapperHasher::operator() being private. Marking that public (changing class to struct does this implicitly) makes the unordered map legal.

std::map argument with empty brace-initializers for default argument segfaults in GCC

Problem
I got a bug report from user reporting a segfault in library I develop.
The minimal example of the faulty code is:
#include <map>
#include <string>
#include <iostream>
void f(std::map<std::string, std::string> m = {})
{
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
int main()
{
f();
}
When compiled with GCC (I tested 4.8.2 and 4.7.3) it correctly prints 0 as size of the container, but segfaults inside the loop (which shouldn't be executed at all).
Workarounds
However, I can fix the problem by changing the declaration to:
void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{})
Copying the map works as well:
void f(std::map<std::string, std::string> mx = {})
{
auto m = mx;
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
Changing the parameter to const std::map<...>& also works.
GCC 4.9.1 works fine.
Clang also compiles and runs the code just fine. (even when using the same libstdc++ as failing gcc 4.8.2)
Working example: http://coliru.stacked-crooked.com/a/eb64a7053f542efd
Question
The map is definitely not in valid state inside the function (details bellow).
It looks like a GCC (or libstdc++) bug, but I want to be sure I'm not making some stupid mistake here.
It's hard to believe such a bug would stay in gcc for at least 2 major version.
So my question is: Is the way of initializing default std::map parameter wrong (and bug in my code) or is it a bug in stdlibc++ (or gcc)?
I'm not looking for workarounds (as I know what to do to make to code work)
When integrated in the application, the offending code executes fine on some computers (even when compiled with gcc 4.8.2) on some doesn't.
Details
I compile it using:
g++-4.8.2 -g -Wall -Wextra -pedantic -std=c++11 /tmp/c.cpp -o /tmp/t
Backtrace from gdb:
#0 std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=..., __str=...) at /usr/src/debug/sys-devel/gcc-4.8.2/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:2758
#1 0x0000000000400f36 in f (m=std::map with 0 elements) at /tmp/c.cpp:9
#2 0x0000000000400fe0 in main () at /tmp/c.cpp:15
/tmp/c.cpp:9 is the line with std::cout << ...
ASAN reports:
AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8
This seems like nullptr - 8
valgrind shows:
==28183== Invalid read of size 8
==28183== at 0x4ECC863: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.2/libstdc++.so.6.0.18)
==28183== by 0x400BD5: f(std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >) (c.cpp:9)
==28183== by 0x400C7F: main (c.cpp:15)
==28183== Address 0xffffffffffffffe8 is not stack'd, malloc'd or (recently) free'd
Looking at internal state of the map shows that the code really has to fail:
std::map::begin() in libstdc++ returns value of
this->_M_impl._M_header._M_parent
from it's internal representation, std::map::end() returns:
&this->_M_impl._M_header
gdb shows:
(gdb) print m._M_t._M_impl._M_header
$5 = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffd6d8, _M_right = 0x7fffffffd6d8}
(gdb) print &m._M_t._M_impl._M_header
$6 = (std::_Rb_tree_node_base *) 0x7fffffffd6a8
So value of begin() and end() are not the same (begin() is nullptr) as mandated by standard for empty std::map.
Looks like this bug was fixed in 4.8.3/4.9.0, the bug report which has a similar example and also seg-faults says:
The attached minimal testcase has the following function with
default-constructed default argument:
void do_something( foo f = {} )
{ std::cout << "default argument is at " << &f << std::endl;
}
The constructor for foo outputs its address; I got the following
output from a single run:
constructed foo # 0x7ffff10bdb7f
default argument is at 0x7ffff10bdb60
It shows that only 1 foo was constructed, and not at the same address
as that of the default argument. It's been a loooong week, but I can't
see anything wrong with the code. In the real code on which this was
based, a segfault was occurring when running the destructor of a foo
that was move-constructed from the default argument, because the
underlying memory was seemingly uninitialised.
We can see from a live example that 4.9.0 does not demonstrate this problem.
We can see this was intentional functionality from defect report 994 and the subsequent resolution N3217:
This paper presents detailed wording changes relative to the current
C++ Working Draft N3126 to implement brace-initializers for default
arguments for functions, as proposed in N3139 "An Incomplete Language
Feature" by Bjarne Stroustrup, thereby also addressing core issue 994.
This is also covered in the proposal N3139: An Incomplete Language Feature.
Interesting to note that Visual Studio also has a bug with respect to brace-initializers as default arguments which I think is still unresolved.

Unordered_map<string, int> works but Unordered_map<string, string> does not

I don't understand why the second block of code in this short example does not compile correctly. It is my understanding that the second parameter in the <> represents the value, which doesn't need to be unique. Why is the second block of code throwing a compiler error, and what do I need to do to remedy it?
// Unordered Map example .cpp
#include <stdio.h>
#include <string>
#include <cstring>
#include <unordered_map>
using namespace std;
int main(void) {
// This works as expected
unordered_map<std::string, int> m;
m["foo"] = 42;
printf("%i\n", m["foo"]);
// This this doesn't compile
unordered_map<std::string, std::string> m1;
m1["foo"] = "42";
printf("%s\n", m1["foo"]);
return 0;
}
I am compiling this code on CentOS 5.8 using
g++44 -c -Wall -std=c++0x -g map_example.cpp
and these are the errors I am getting
map_example.cpp: In function ‘int main()’:
map_example.cpp:20: warning: cannot pass objects of non-POD type ‘struct std::basic_string<char, std::char_traits<char>, std::allocator<char> >’ through ‘...’; call will abort at runtime
map_example.cpp:20: warning: format ‘%s’ expects type ‘char*’, but argument 2 has type ‘int’
If I am having trouble with a basic c++ class such a std:string what do I need to do to have a custom class as a value, where can I find a fully implemented minimal example?
printf does not work with std::string. Either use cout << m1["foo"] or printf("%s", m1["foo"].c_str())
printf("%s\n", m1["foo"]); is C.
For c++ you should use std::cout to have both the string and the int map's values printed out as expected.
The compiler cannot do a translation for a std::string object automatically to const char* (there is no conversion to const char* by default: Why does std::string not provide a conversion to const char*?)

Invalid conversion to non-const reference in C++0x... bug in std::pair?

I have been developing a library that is getting pretty large, and now I am adding some template-based parts that use C++0x features. So I tried to compile my library (which compiled entirely without warnings on the current standard) with the flag -std=c++0x using gcc version 4.4.5 (on Linux). Now I got a huge influx of error messages related to the conversion of "temporaries" variables to non-const references. The problem is, they are not temporary!
Here is a small piece of code that reproduces the error:
#include <iostream>
#include <map>
struct scanner {
scanner& operator &(std::pair<std::string, int&> i) {
std::cout << "Enter value for " << i.first << ": ";
std::cin >> i.second;
return *this;
};
};
struct vect {
int q[3];
void fill(scanner& aScan) {
aScan & std::pair<std::string, int&>("q0",q[0])
& std::pair<std::string, int&>("q1",q[1])
& std::pair<std::string, int&>("q2",q[2]);
};
};
int main() {
vect v;
scanner s;
v.fill(s);
return 0;
};
If you compile this with the current standard (no c++0x flag) it will compile and run as expected. However, if you compile it with -std=c++0x, it will throw the following error at compile-time:
/usr/include/c++/4.4/bits/stl_pair.h:94: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int’
I really can't figure this out. I have looked over the web and SO, but none seem to have this problem. Is it a bug in std::pair? I would really like to know what the problem is.. thank you for any insight you can give.
PS: don't complain about the "quality" or "stupidity" of the code above, it is not real code.. just an example that shows the error.
Your code is not valid C++03, comeau gives (after adding a return statement to op&):
"stl_pair.h", line 44: error: qualifiers dropped in binding reference of
type "int &" to initializer of type "const int"
pair(const _T1& __a, const _T2& __b) : first(__a), second(__b) {}
^
detected during instantiation of "std::pair<_T1, _T2>::pair(const
_T1 &, const _T2 &) [with _T1=std::string, _T2=int &]" at
line 17 of "ComeauTest.c"
...
The problem is a reference inside a pair. If I remember correctly, it is a gcc extension that allows this. Gcc does accept it with -std=gnu++98, which is the default for C++, but with -std=c++98, gcc 4.4.3 gives:
In file included from /usr/include/c++/4.4/bits/stl_algobase.h:66,
from /usr/include/c++/4.4/algorithm:61,
from PREAMBLE:7:
/usr/include/c++/4.4/bits/stl_pair.h: In instantiation of ‘std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int&>’:
<input>:5: instantiated from here
/usr/include/c++/4.4/bits/stl_pair.h:83: error: forming reference to reference type ‘int&’
...
There are LOTS of bugs in gcc C++0x support, it's very much unfinished and development is ongoing. This is doubly true for a version as old as gcc-4.4.5. If you're serious about starting C++0x development before the standard is ratified, you need to use the bleeding-edge version of the compiler and standard library.
It compiles fine both with and without -std=c++0x with GCC 4.5.2.
I guess GCC 4.4.5 doesn't support C++0x that much to get this working.
Except for the missing return *this; in scanner& operator &(std::pair<std::string, int&> i), your code is valid C++0x.

Problem with std::make_tuple in C++0x

Tying to compile the following program with Visual Studio 10, I get lot of compile errors:
#include "stdafx.h"
#include <tuple>
#include <string>
#include <map>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
typedef std::tuple<std::string, std::string> key_t;
typedef std::map<key_t, std::string> map_t;
map_t the_map;
auto k = std::make_tuple("one", "two");
the_map[k] = "the value";
auto q = std::make_tuple("one", "two");
auto i = the_map.find(q);
std::cout << i->second << std::endl;
return 0;
}
Error 1 error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'const key_t' to 'const std::basic_string<_Elem,_Traits,_Ax> &' c:\program files (x86)\microsoft visual studio 10.0\vc\include\tuple 127 1 tuple
Coming from the line:
std::cout << i->second << std::endl;
Strange thing is, as least from my point of view, if I change these lines:
auto k = std::make_tuple("one", "two");
the_map[k] = "the value";
to
the_map[std::make_tuple("one", "two")] = "p";
the program compiles. So my question is of course why? I guess it has something to do with make_tuple and move semantics - but I do not understand what..
Apparently the error comes in fact from the line the_map[k] = "the value";
When you use the [] operator on a map, the library tries to create a std::pair<Key,Value> object. In your case, this becomes std::pair<std::tuple<std::string,std::string>,std::string>.
However if you use an intermediate variable k, the constructor of std::pair which is called is: (copy-pasted from the standard lib)
_Pair_base(const _Ty1x& _Val1, _Ty2x&& _Val2)
: first(_Val1), second(_STD move(_Val2))
{ // construct from specified values
}
This constructor is trying to make a copy of your key_t. Unfortunatly, the tuple implementation of MSVC++ is bugged at the moment and the copy fails to compile (see also this: C++0x : are tuples of tuples allowed?)
I can diagnosize more, because this implementation is not only bugged but also very complicated.
Boost's tuples should work but don't have an < operator, so you can't use them.
The "best" solution for the moment is to write the_map.insert(std::make_pair(k, "the value"));
This looks like a bug in VS10, for some reason it's trying to cast the key type to the value type.
This simplified version also fails.
typedef std::map<std::tuple<int, int>, int> map_t;
map_t the_map;
map_t::key_type k = std::make_tuple(1,2);
the_map[k] = 3;
Produces the following:
error C2440: 'initializing' : cannot convert from 'const std::tr1::tuple<_Arg0,_Arg1>' to 'int'