I have this function that converts an integer to a std::string:
std::string intToStr(const int n) {
stringstream ss;
ss << n;
return ss.str();
}
It's worked well so far, but now I'm trying to construct a string to put into a std::pair, and I'm having some trouble.
Given an integer variable hp and a function that returns an integer int maxHP(), I want to construct a string that looks like this: "5/10" (if hp is 5 and maxHP returns 10).
here's my attempt:
string ratio = intToStr(hp) + "/" + intToStr(maxHP());
return pair<string, OtherType>(ratio, someOtherType);
Compiling with g++, it fails with this error:
src/Stats.cpp:231: error: no matching function for call to
‘std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
TCODColor>::pair(<unresolved overloaded function type>, const TCODColor&)’
/usr/include/c++/4.4/bits/stl_pair.h:92: note: candidates are: std::pair<_T1,
_T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = std::basic_string<char,
std::char_traits<char>, std::allocator<char> >, _T2 = TCODColor]
/usr/include/c++/4.4/bits/stl_pair.h:83: note: std::pair<_T1,
_T2>::pair(const _T1&, const _T2&) [with _T1 = std::basic_string<char,
std::char_traits<char>, std::allocator<char> >, _T2 = TCODColor]
/usr/include/c++/4.4/bits/stl_pair.h:79: note: std::pair<_T1,
_T2>::pair() [with _T1 = std::basic_string<char, std::char_traits<char>,
std::allocator<char> >, _T2 = TCODColor]
/usr/include/c++/4.4/bits/stl_pair.h:68: note:
std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
TCODColor>::pair(const std::pair<std::basic_string<char, std::char_traits<char>,
std::allocator<char> >, TCODColor>&)
so std::pair doesn't like my string. I've confirmed that it's not OtherType causing the problem, because I have another pair constructor that compiles fine:
pair<string, OtherType>("astring", someOtherType);
Anyone see how I can fix this?
Fixed it, though the answer was odd. My problem was that somehow ratio wasn't getting defined, but g++ didn't tell me about it. Changing my code to use make_pair as GMan suggested suddenly made it tell me. Anyone know why that would happen?
Here's more of the function:
if(verbose)
string ratio = intToStr(hp) + "/" + intToStr(maxHP());
if(div > 1.0f) {
if(verbose) return pair<string, OtherType>(ratio, someOtherType); // doesn't compile
else return pair<string, OtherType("astring", someOtherType); // compiles
}
here's the fixed code:
string ratio = intToStr(hp) + "/" + intToStr(maxHP());
if(div > 1.0f) {
if(verbose) return make_pair(ratio, someOtherType); // compiles now
else return make_pair("astring", someOtherType); // compiles
}
The reason that this fails:
if(verbose)
string ratio = intToStr(hp) + "/" + intToStr(maxHP());
// the block of the if is now over, so any variables defined in it
// are no longer in scope
is that a variable's scope is limited to the block it is defined in.
Here's how I would fix your code:
if (div > 1.0f)
{
string str = verbose ? intToStr(hp) + "/" + intToStr(maxHP()) : "astring";
return make_pair(str, someOtherType);
}
A little more concise. Also, your formatting could be made a bit more generic.
It looks like it might be a const issue. Look at the overloads it's reporting in the error:
pair(std::pair<_T1, _T2>&&)
pair(const _T1&, const _T2&)
So it looks like it's expecting const parameters.
Related
In order to prevent the compiler to apply e.g. a std::vector<T> to a statement like std::cout << u, I would like to do something like this:
if constexpr (std::has_output_operator<U>) {
std::cout << u;
}
Is there some way to achieve this?
EDIT (clarification)
I am working on printf-like function which can also print strings and vectors of POD and strings (and vectors thereof).
I would like to extend this functionality to any type which has an output operator.
The actual formatting for non-vector types is done by the functions simpleFormat():
// simpleFormat
// special case for single string
//
std::string simpleFormat(const std::string sFormat, const std::string t) {
size_t required = snprintf(NULL, 0, sFormat.c_str(), t.c_str());
char sTemp[required+1];
sprintf(sTemp, sFormat.c_str(), t.c_str());
return std::string(sTemp);
}
// simpleFormat
// catch for vectors (should not be sent to simpleFormat)
template<typename T>
std::string simpleFormat(const std::string sFormat, const std::vector<T> t) {
return "";
}
// simpleFormat
// formatting PODs and Objects with output operator a char using
template<typename T>
std::string simpleFormat(const std::string sFormat, const T t) {
std::string sRes = "";
if (sFormat.size() > 0) {
if (sFormat != "%O") {
size_t required = snprintf(NULL, 0, sFormat.c_str(), t);
char sTemp[required+1];
sprintf(sTemp, sFormat.c_str(), t);
sRes = std::string(sTemp);
} else {
std::stringstream ss("");
ss << t;
sRes += ss.str();
}
}
return sRes;
}
When i compile this for some applications, get the error
In file included from AgentCounter.cpp:6:
../utils/stdstrutilsT.h: In instantiation of ‘std::string simpleFormat(std::string, T) [with T = __gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; std::string = std::__cxx11::basic_string<char>]’:
../utils/stdstrutilsT.h:195:33: required from ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = __gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >; Args = {long long unsigned int}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
../utils/stdstrutilsT.h:281:31: required from ‘std::string stdsprintf(std::string, Args ...) [with Args = {__gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, long long unsigned int}; std::string = std::__cxx11::basic_string<char>]’
../utils/stdstrutilsT.h:291:34: required from ‘void stdprintf(std::string, Args ...) [with Args = {__gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, long long unsigned int}; std::string = std::__cxx11::basic_string<char>]’
AgentCounter.cpp:326:22: required from here
../utils/stdstrutilsT.h:165:28: error: no match for ‘operator<<’ (operand types are ‘std::stringstream’ {aka ‘std::__cxx11::basic_stringstream<char>’} and ‘const __gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >’)
165 | ss << t;
| ~~~^~~~
Even though i have a simpleFormat() variant for vectors, the compiler still wants to fit the std::vector into the POD variant.
That is why i hope there is constexpr which lets me find out if the passed type has an output operator.
Of course if there are other possibilities to stop the compiler from applying vectors to my non-vector function, i'd like to learn about them.
This can be done straightforwardly using the C++20 requires-expression, which checks whether its operand is valid:
if constexpr (requires { std::cout << u; })
You can also define a named concept with the requires-expression and then use it in place of the requires-expression every time you need it.
This fail to compile in gcc 4.1.2 / RedHat 5 :
#include <string>
#include <vector>
#include <map>
class Toto {
public:
typedef std::string SegmentName;
};
class Titi {
public:
typedef Toto::SegmentName SegmentName; // import this type in our name space
typedef std::vector<SegmentName> SegmentNameList;
SegmentNameList segmentNames_;
typedef std::map<SegmentName, int> SegmentTypeContainer;
SegmentTypeContainer segmentTypes_;
int getNthSegmentType(unsigned int i) const {
int result = -1;
if(i < segmentNames_.size())
{
SegmentName name = segmentNames_[i];
result = segmentTypes_[ name ];
}
return result;
}
};
The error is :
error: no match for 'operator[]' in '(...)segmentTypes_[name]'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:340:
note: candidates are: _Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&)
[with _Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = int, _Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> >]
Why ? The map is rather straightforward. I guess this has to do with the typedefs, but what is wrong ?
[edit] Even if I remove all the typedefs and use std::string everywhere, the problem persists... Am I misusing maps ?
std::map::operator[] is non-const and you're trying to use it from a const method.
You could achieve this using std::map::find, which returns a const_iterator:
SegmentTypeContainer::const_iterator iter = segmentTypes_.find(name);
If you're using C++11, you could also use std::map::at, which will throw an exception if the key is not found in the map:
result = segmentTypes_.at(name);
std::map::operator[] is not a const method, but you are calling it from a const method of your class. The reason for this is that it adds an element if the key is not present.
You can use C++11 at():
result = segmentTypes_.at(name); // throws exception if key not present.
or use std::map::find.
SegmentTypeContainer::const_iterator it = segmentTypes_.find(name);
if (it != segmentTypes_.end())
{
// OK, element with key name is present
result = it->second;
}
I have the following code, which works fine, however when I change the iter getB(string& s , string& tag); function definition to iter getB(const string& s , const string& tag); I get the error pasted at the end. I believe the = operator is not defined because with this new function definition, if I don't assign the result of search to i the program compiles, though later resulting in a segmentation fault, which I believe is expected. Can someone explain to me why I can't assign the result of search when the function definition contains const keywords. Thank you.
the code:
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef string::iterator iter;
iter getB(string& s , string& tag);
int main (){
string line = "hello, how are you?";
string tag = "how";
iter i = getB(line,tag);
for(i ; i!=line.end(); i++){
cout << *i ;
}
cout << endl;
return 0;
}
iter getB(string& s , string& tag)
{
iter i;
i = search(s.begin() , s.end() , tag.begin() , tag.end());
return i;
}
~
~
the error message with the altered function definition:
test1.cpp: In function ‘iter getB(const std::string&, const std::string&)’:
test1.cpp:24: error: no match for ‘operator=’ in ‘i = std::search [with _ForwardIterator1 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _ForwardIterator2 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >](((const std::string*)s)->std::basic_string<_CharT, _Traits, _Alloc>::begin [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>](), ((const std::string*)s)->std::basic_string<_CharT, _Traits, _Alloc>::end [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>](), ((const std::string*)tag)->std::basic_string<_CharT, _Traits, _Alloc>::begin [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>](), ((const std::string*)tag)->std::basic_string<_CharT, _Traits, _Alloc>::end [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]())’
/usr/include/c++/4.2.1/bits/stl_iterator.h:637: note: candidates are: __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >& __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator=(const __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)
For a const string, begin() and end() return std::string::const_iterator not std::string::iterator. You can convert an iterator to a const_iterator, but not vice versa.
The quick solution would be to change your typedef:
typedef string::const_iterator iter;
If s is a const string &, then s.begin(), and hence the return type of search, is string::const_iterator. This is not convertible to string::iterator, which is the type of i.
This is as it should be, since otherwise that conversion would break const-correctness by allowing you to modify the string. You should change the type of i to string::const_iterator, or perhaps auto if you're using C++11.
if you pass in a const iterator to std::search, then the return value is also a const iterator (it will be of the same type as the first iterator passed in)
change this:
typedef string::iterator iter;
to this:
typedef string::const_iterator iter;
Here is the code:
#include <iostream>
#include <string>
#include <map>
#include <stdexcept>
#include <boost/ptr_container/ptr_vector.hpp>
struct TestStruct
{
std::string str1;
int var1;
};
struct Config
{
// Map
typedef std::map< std::string, boost::ptr_vector<struct TestStruct> > testMap;
};
void foo(Config& config)
{
if (config.testMap.empty())
{
std::cout << "It worked!" << std::endl;
}
else
{
std::cout << "It didn't work!" << std::endl;
}
return;
}
int testMain(void)
{
Config config;
foo(config);
return;
}
int main(void)
{
try
{
return testMain(/*argc, argv*/);
}
catch(std::exception& err)
{
std::cerr << "Error running program: " << err.what() << std::endl;
return 1;
}
catch(...)
{
std::cerr << "Program failed with an unknown exception." << std::endl;
return 1;
}
}
I'm new to maps - never used one before. I've found many examples of how to use them online. Unfortunately I can't seem to makes sense of more advanced examples of them.
What I'd like to do is create a map with a key (std::string) and a value (boost::ptr_vector<struct>).
I was going to start by just declaring and passing it around successfully. Then I wanted try and figure out how to fill it up.
I ran into an error that is just vague enough I don't know how to interpret it.
Any suggestions on what I've done wrong in the "use" of testMap?
Also, can someone provide some simple example of how I can populate the map up.
Say I want a key of a and a value of str1 = "hello", var1 = 10. How would I do this?
FOLLOW UP QUESTION:
In regards to the answer left below by Kerrek SB.
If I do the following...
std::string key = "a";
TestStruct value = {"hello", 10};
config.testMap[key] = value;
I get the following error:
error: no match for 'operator=' in 'config->Config::testMap.std::map<_Key, _Tp, _Compare, _Alloc>::operator[] [with _Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = boost::ptr_vector<TestStruct, boost::heap_clone_allocator, std::allocator<void*> >, _Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, boost::ptr_vector<TestStruct, boost::heap_clone_allocator, std::allocator<void*> > > >](((const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)((const std::basic_string<char, std::char_traits<char>, std::allocator<char> >*)(& key)))) = value'
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:45: note: candidates are: boost::ptr_vector<T, CloneAllocator, Allocator>& boost::ptr_vector<T, CloneAllocator, Allocator>::operator=(std::auto_ptr<boost::ptr_vector<T, CloneAllocator, Allocator> >) [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:45: note: boost::ptr_vector<T, CloneAllocator, Allocator>& boost::ptr_vector<T, CloneAllocator, Allocator>::operator=(boost::ptr_vector<T, CloneAllocator, Allocator>) [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
If instead I do the .insert() method I get the following error:
instantiated from here
/opt/csw/gcc4/lib/gcc/sparc-sun-solaris2.8/4.3.2/../../../../include/c++/4.3.2/bits/stl_pair.h:106: error: no matching function for call to 'boost::ptr_vector<TestStruct, boost::heap_clone_allocator, std::allocator<void*> >::ptr_vector(const TestStruct&)'
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:50: note: candidates are: boost::ptr_vector<T, CloneAllocator, Allocator>::ptr_vector(typename boost::ptr_sequence_adapter<T, std::vector<void*, Allocator>, CloneAllocator>::size_type, const typename boost::ptr_sequence_adapter<T, std::vector<void*, Allocator>, CloneAllocator>::allocator_type&) [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:45: note: boost::ptr_vector<T, CloneAllocator, Allocator>::ptr_vector(std::auto_ptr<boost::ptr_vector<T, CloneAllocator, Allocator> >) [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:45: note: boost::ptr_vector<T, CloneAllocator, Allocator>::ptr_vector(const typename boost::ptr_sequence_adapter<T, std::vector<void*, Allocator>, CloneAllocator>::allocator_type&) [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:45: note: boost::ptr_vector<T, CloneAllocator, Allocator>::ptr_vector() [with T = TestStruct, CloneAllocator = boost::heap_clone_allocator, Allocator = std::allocator<void*>]
/usr/local/boost/boost_1_38_0_gcc4/include/boost/ptr_container/ptr_vector.hpp:35: note: boost::ptr_vector<TestStruct, boost::heap_clone_allocator, std::allocator<void*> >::ptr_vector(const boost::ptr_vector<TestStruct, boost::heap_clone_allocator, std::allocator<void*> >&)
FOLLOW UP:
From what I've researched this is not possible.
You cannot use a ptr_vector in a map. (so I've been told) Due to ownership/copy issues?
"What happens when you attempt to copy a ptr_vector, which is bound to happen inside a map? The pointer containers model exclusive ownership of pointers.
You can do this with C++0x and std::move, but that won’t help you here."
Can anyone provide a counter example?
Your class Config doesn't contain any members! (Just a typedef.) Define a member, too:
struct Config
{
typedef std::map<std::string, boost::ptr_vector<TestStruct> > testMap_type;
testMap_type testMap; // member
};
Also, in C++ there is no need to say struct TestStruct, just say TestStruct.
To add an element:
std::string key = "xxx";
TestStruct val = { "hello", 10 };
// Insert with []-operator
config.testMap[key] = val;
// Insert with insert():
config.testMap.insert(std::pair<std::string, TestStruct>(key, val)); // alternative
Edit: Sorry, I misrepresented your actual data structure. Here's an example with a std::vector:
typedef std::map<std::string, std::vector<int>> MyMap;
MyMap m;
m["hello"].push_back(1);
m["hello"].push_back(2);
m["world"].push_back(3);
In your case, you could say config.testMap[key].push_back(val), or you could make a new vector:
boost::ptr_vector<TestStruct> new_v;
// populate new_v;
config.testMap.insert(std::pair<std::string, boost::ptr_vector<TestStruct>>(key, new_v);
From what I've researched this is not possible.
You cannot use a ptr_vector in a map. (so I've been told) Due to ownership/copy issues?
"What happens when you attempt to copy a ptr_vector, which is bound to happen inside a map? The pointer containers model exclusive ownership of pointers.
You can do this with C++0x and std::move, but that won’t help you here."
The following code does not want to compile. See the included error message.
Code:
#include <map>
#include <vector>
#include <iostream>
class MapHolder {
public:
std::map<std::vector<std::string>,MapHolder> m_map;
void walk_through_map() {
std::map<std::vector<std::string>,MapHolder>::iterator it;
for(it = m_map.begin(); it < m_map.end(); ++it) {
it->second.print();
}
}
void print() { std::cout << "hey" << std::endl; }
};
int
main(int argc, char *argv[])
{
MapHolder m;
m.walk_through_map();
}
Error:
$ g++ test.cc -O test
test.cc: In member function 'void MapHolder::walk_through_map()':
test.cc:12: error: no match for 'operator<' in 'it < ((MapHolder*)this)->MapHolder::m_map.std::map<_Key, _Tp, _Compare, _Alloc>::end [with _Key = std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, _Tp = MapHolder, _Compare = std::less<std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, _Alloc = std::allocator<std::pair<const std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, MapHolder> >]()'
I used this type of map and iterating process multiple times before. What's the problem here? How can it be solved.
(The code looks meaningless, but this is a reduced sample which is still supposed to work)
Use != instead of < in iterator comparison.
operator< is only available for random access iterators. Since std::maps are usually implemented using some sort of balanced tree, there is usually no fast way to find out whether one iterator points to an element before another one (though end is an exception).
I guess the reasoning behind this is that these mysterious compiler errors force you to think about your code again and implement operator< yourself if you find that this is the best way to solve your problem.