Possible to create std::map as rvalue? - c++

I'm not sure if this is an error with my C++ syntax, or if this is something that cannot be accomplished at all.
I want to define a class that takes a std::map as a constructor argument. I then want to create an instance of that class by passing a "temporary" (appropriate to call this "rvalue"?) std::map. I.e. I do not want to create an lvalue std::map and then pass that to the constructor.
Can this be accomplished? I have tried the following (commented lines show failed attempts)
#include <map>
#include <string>
#include <iostream>
class Test
{
public:
Test(std::map<int,char>& rMap)
{
std::map<int,char>::iterator iter;
for (iter = rMap.begin(); iter != rMap.end(); ++iter)
{
mMap[iter->first] = mMap[iter->second];
}
}
virtual ~Test(){}
protected:
std::map<int, char> mMap;
};
int main()
{
std::cout << "Hello world!" << std::endl;
//Test test({1,'a'}); // Compile error.
//Test test(std::map<int,char>(1,'a')); // Also compile error.
//Test test(std::map<int,char>{1,'a'}); // Yet again compile error.
return 0;
}
This is my compiler:
g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11)
Compile errors can be posted upon request, but I'm not sure if they would be useful if my problem is syntactic.
Thank you.

Do
Test(std::map<int, char> rMap) : mMap(std::move(rMap)) {}
or
Test(std::map<int, char>&& rMap) : mMap(std::move(rMap)) {}
or
Test(const std::map<int, char>& rMap) : mMap(rMap) {}
Temporary cannot bind to non const l-value reference.
And use it as
Test test({{1,'a'}});
Test test2({{1,'a'}, {2, 'b'}});

Yes but your constructor takes an lvalue reference. It must instead be a reference-to-const, or an rvalue reference.
Just like with any other type.

#include <map>
class Test {
public:
Test(std::map<int,char> const& cMap)
{
std::map<int,char>::const_iterator iter;
for (iter = cMap.cbegin(); iter != cMap.cend(); ++iter)
{
mMap[iter->first] = mMap[iter->second];
}
}
virtual ~Test() { }
protected:
std::map<int, char> mMap;
};
int main() {
Test test(std::map<int,char>({{1,'a'}, {2, 'b'}}));
return 0;
}
Some explanations:
You need Test(T), Test(const T&) or Test(T&&) constructor if you want to pass temporary object (like std::map<int,char>()) into it. Don't forget what T&& constructors is used for. If in doubt do not use them.
If you work with const std::vector/map/list/... you can't use .begin() and .end() to iterate through elements -- use .cbegin() and .cend(). Moreover it will become more easy with auto. Just try
for (auto iter = rMap.cbegin(); iter != rMap.cend(); ++iter)
instead
std::map<int,char>::const_iterator iter;
for (iter = rMap.cbegin(); iter != rMap.cend(); ++iter)
To initialize std::map use "double-bracket" initializers like {{1,'a'}, {2, 'b'}} -- {{key, value}, {second_key, second_value}, ...} this construction is available because
1) std::map has this constructor:
map( std::initializer_list<value_type> init,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
// see http://en.cppreference.com/w/cpp/container/map/map
2) inner braces {1,'a'} is interpreted as value_type constructor call. And value_type for std::map<int, char> is std::pair<int, char>.
P. S. don't forget that std::map<int,char>() and std::map<int,char>{} constructor calls are equal. That means you can drop front brackets: std::map<int,char>({{1,'a'},{2,'b'}}) --> std::map<int,char>{{1,'a'},{2,'b'}}

You should use:
class Test
{
public:
Test(const std::map<int,char>& rMap) : mMap(rMap)
{}
Test(std::map<int,char>&& rMap) : mMap(std::move(rMap))
{}
virtual ~Test(){}
protected:
std::map<int, char> mMap;
};
This provides two constructors: one which takes a const lvalue reference and copies its contents into mMap and one which takes an rvalue reference and moves its contents into mMap. This allows you to construct a Test instance using either a named std::map or a temporary:
int main()
{
std::map<int,char> m{{1, 'a'}, {2,'b'}};
Test t1{m};
Test t2{{{1, 'a'}, {2, 'b'}}};
}
This lets you avoid unnecessary copies when you can, but still lets you make a copy of the map when you need to.

Related

How to retreive a specific element from a vector and assign it to a variable? [duplicate]

I'm trying to figure out a nice way to find the index of a certain object in a vector - by comparing a string to a member field in the object.
Like this:
find(vector.begin(), vector.end(), [object where obj.getName() == myString])
I have searched without success - maybe I don't fully understand what to look for.
You can use std::find_if with a suitable functor. In this example, a C++11 lambda is used:
std::vector<Type> v = ....;
std::string myString = ....;
auto it = find_if(v.begin(), v.end(), [&myString](const Type& obj) {return obj.getName() == myString;})
if (it != v.end())
{
// found element. it is an iterator to the first matching element.
// if you really need the index, you can also get it:
auto index = std::distance(v.begin(), it);
}
If you have no C++11 lambda support, a functor would work:
struct MatchString
{
MatchString(const std::string& s) : s_(s) {}
bool operator()(const Type& obj) const
{
return obj.getName() == s_;
}
private:
const std::string& s_;
};
Here, MatchString is a type whose instances are callable with a single Type object, and return a boolean. For example,
Type t("Foo"); // assume this means t.getName() is "Foo"
MatchString m("Foo");
bool b = m(t); // b is true
then you can pass an instance to std::find
std::vector<Type>::iterator it = find_if(v.begin(), v.end(), MatchString(myString));
In addition to the Lambda and the handwritten functor used by juancho, you have the possibility to use boost::bind (C++03) or std::bind (C++11) and a simple function:
bool isNameOfObj(const std::string& s, const Type& obj)
{ return obj.getName() == s; }
//...
std::vector<Type>::iterator it = find_if(v.begin(), v.end(),
boost::bind(&isNameOfObj, myString, boost::placeholders::_1));
Or, if Type has a method isName:
std::vector<Type>::iterator it = find_if(v.begin(), v.end(),
boost::bind(&Type::isName, boost::placeholders::_1, myString));
This is just for completeness. In C++11 I'd prefer Lambdas, in C++03 I'd use bind only if the comparison function itself exists already. If not, prefer the functor.
PS: Since C++11 has no polymorphic/templated lambdas, bind still has it's place in C++11, e.g. if the parameter types are unknown, hard to spell, or otherwise not easy to deduce.
A simple iterator may help.
typedef std::vector<MyDataType> MyDataTypeList;
// MyDataType findIt should have been defined and assigned
MyDataTypeList m_MyObjects;
//By this time, the push_back() calls should have happened
MyDataTypeList::iterator itr = m_MyObjects.begin();
while (itr != m_MyObjects.end())
{
if(m_MyObjects[*itr] == findIt) // any other comparator you may want to use
// do what ever you like
}

unordered_set of shared_ptr does not find equivalent objects it has stored

I have a class that stores a std::vector of stuff. In my program, I create a std::unordered_set of std::shared_ptr to objects of this class (see code below). I defined custom functions to compute hashes and equality so that the unordered_set "works" with the objects instead of the pointers. This means: Two different pointers to different objects that have the same content should be treated as equal, let's call it "equivalent".
So far everything worked as expected but now I stumbled across a strange behaviour: I add a pointer to an object to the unordered_set and create a different pointer to a different object with the same content. As said I would expect that my_set.find(different_object) would return a valid iterator to the equivalent pointer stored in the set. But it doesn't.
Here is a minimal working code example.
#include <boost/functional/hash.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <unordered_set>
#include <vector>
class Foo {
public:
Foo() {}
bool operator==(Foo const & rhs) const {
return bar == rhs.bar;
}
std::vector<int> bar;
};
struct FooHash {
size_t operator()(std::shared_ptr<Foo> const & foo) const {
size_t seed = 0;
for (size_t i = 0; i < foo->bar.size(); ++i) {
boost::hash_combine(seed, foo->bar[i]);
}
return seed;
}
};
struct FooEq {
bool operator()(std::shared_ptr<Foo> const & rhs,
std::shared_ptr<Foo> const & lhs) const {
return *lhs == *rhs;
}
};
int main() {
std::unordered_set<std::shared_ptr<Foo>, FooHash, FooEq> fooSet;
auto empl = fooSet.emplace(std::make_shared<Foo>());
(*(empl.first))->bar.emplace_back(0);
auto baz = std::make_shared<Foo>();
baz->bar.emplace_back(0);
auto eqFun = fooSet.key_eq();
auto hashFun = fooSet.hash_function();
if (**fooSet.begin() == *baz) {
std::cout << "Objects equal" << std::endl;
}
if (eqFun(*fooSet.begin(), baz)) {
std::cout << "Keys equal" << std::endl;
}
if (hashFun(*fooSet.begin()) == hashFun(baz)) {
std::cout << "Hashes equal" << std::endl;
}
if (fooSet.find(baz) != fooSet.end()) {
std::cout << "Baz in fooSet" << std::endl;
} else {
std::cout << "Baz not in fooSet" << std::endl;
}
return 0;
}
Output
Objects equal
Keys equal
Hashes equal
And here is the problem:
Baz not in fooSet
What am I missing here? Why does the set not find the equivalent object?
Possibly of interest: I played around with this and found that if my class stores a plain int instead of a std::vector, it works. If I stick to the std::vector but change my constructor to
Foo(int i) : bar{i} {}
and initialize my objects with
std::make_shared<Foo>(0);
it also works. If I remove the whole pointer stuff, It breaks as std::unordered_set::find returns constant iterators and thus modification of objects in the set cannot be done (this way). However, none of these changes is applicable in my real program, anyway.
I compile with g++ version 7.3.0 using -std=c++17
You can't modify an element of a set (and expect the set to work). Because you have provided FooHash and FooEq which inspect the referent's value, that makes the referent part of the value from the point of view of the set!
If we change the initialisation of fooSet to set up the element before inserting it, we get the result you want/expect:
std::unordered_set<std::shared_ptr<Foo>, FooHash, FooEq> fooSet;
auto e = std::make_shared<Foo>();
e->bar.emplace_back(0); // modification is _before_
fooSet.insert(e); // insertion
Looking up the object in the set depends on the hash value not changing. If we really need to modify a member after it has been added, we need to remove it, make the changes, then add the modified object - see Yakk's answer.
To avoid running into issues like this, it may be safer to use std::shared_ptr<const Foo> as elements, which will prevent modification of the pointed-at Foo through the set (although you're still responsible for the use of any non-const pointers you may also have).
Any operation such that the hash or == result of an element in an unordered_set violates the rules of unordered_set is bad; the result is undefined behavior.
You changed the result of a hash of an element in an unordered_set, because your elements are shared pointers, but their hash and == is based off of the value pointed to. And your code changes the value pointed to.
Make all std::shared_ptr<Foo> in your code std::shared_ptr<Foo const>.
This includes the equals and hash code and unordered set code.
auto empl = fooSet.emplace(std::make_shared<Foo>());
(*(empl.first))->bar.emplace_back(0);
this code is right out, and it will (afterwards) fail to compile, as is safe.
If you want to mutate an element in a fooSet,
template<class C, class It, class F>
void mutate(C& c, It it, F&& f) {
auto e = *it->first;
f(e); // do this before erasing, more exception-safe
auto new_elem = std::make_shared<decltype(e)>(std::move(e));
c.erase(it);
c.insert( new_elem ); // could throw, but hard to avoid.
}
now the code reads:
auto empl = fooSet.emplace(std::make_shared<Foo>());
mutate(fooSet, empl.first, [&](auto&& elem) {
elem.emplace_back(0);
});
mutate copies an element out, removes the pointer from the set, calls the function on it, then reinserts it back into the fooSet.
Of course in this case it is dumb; we just put it in and now we take it out mutate it and put it back.
But in a more general case it will be less dumb.
Here you add an object and it's stored using its current hash value.
auto empl = fooSet.emplace(std::make_shared<Foo>());
Here you change the hash value:
(*(empl.first))->bar.emplace_back(0);
The set now has an object stored using the old/wrong hash value. If you need to change anything in an object that affects its hash value, you need to extract the object, change it and re-insert it. If all mutable members of the class are used to calculate the hash value, make it a set of <const Foo> instead.
To make future declarations of sets of shared_ptr<const Foo> easier, you may also extend the std namespace with your specializations.
class Foo {
public:
Foo() {}
size_t hash() const {
size_t seed = 0;
for (auto& b : bar) {
boost::hash_combine(seed, b);
}
return seed;
}
bool operator==(Foo const & rhs) const {
return bar == rhs.bar;
}
std::vector<int> bar;
};
namespace std {
template<>
struct hash<Foo> {
size_t operator()(const Foo& foo) const {
return foo.hash();
}
};
template<>
struct hash<std::shared_ptr<const Foo>> {
size_t operator()(const std::shared_ptr<const Foo>& foo) const {
/* A version using std::hash<Foo>:
std::hash<Foo> hasher;
return hasher(*foo);
*/
return foo->hash();
}
};
template<>
struct equal_to<std::shared_ptr<const Foo>> {
bool operator()(std::shared_ptr<const Foo> const & rhs,
std::shared_ptr<const Foo> const & lhs) const {
return *lhs == *rhs;
}
};
}
With that in place, you can simply declare your unordered_set like this:
std::unordered_set<std::shared_ptr<const Foo>> fooSet;
which now is the same as declaring it like this:
std::unordered_set<
std::shared_ptr<const Foo>,
std::hash<std::shared_ptr<const Foo>>,
std::equal_to<std::shared_ptr<const Foo>>
> fooSet;

l-value specifies const object when using std::make_pair

struct MapInserter
{
private:
int count;
public:
explicit MapInserter()
: count(0)
{
}
std::pair<int, std::string> operator()(std::string& value)
{
return std::make_pair(count++, value);
}
};
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), map.begin(), inserter);
for (auto it = map.begin(), end = map.end(); it != end; ++it)
cout << it->first << " : " << it->second << endl;
return 0;
that's the code. VS returns a compile error regarding l-value specifies const object.
Clicking the error moves you to the following code in a file named utility
template<class _Other1,
class _Other2>
_Myt& operator=(pair<_Other1, _Other2>&& _Right)
{ // assign from moved compatible pair
first = _STD forward<_Other1>(_Right.first);
second = _STD forward<_Other2>(_Right.second);
return (*this);
}
at first, i've had the operator() take const std::string& so I removed the const, because it's obviously talking about the make_pair function. But it still hasn't gone away. Can anyone point me to what this error is about?
The problem is that std::transform() will try to assign to existing elements of the target container. Keys of a map are constant and cannot be assigned to, which is why you're getting a compiler error. But even if they were, you'd get undefined behavior at run-time here, because the target container is empty, and std::transform() would expect it to contain as many elements as the input range.
You should use std::inserter() to create an inserter iterator, like so:
vector<std::string> words = { "one", "two", "three","four","five" };
std::map<int, std::string> map;
MapInserter inserter;
transform(words.begin(), words.end(), std::inserter(map, map.begin()), inserter);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is a live example.
Moreover, taking the value string by mutable lvalue reference in the call operator of your MapInserter is not a good idea: you don't want the argument to be modified, so you should either take it by const& or - my advice - take it by value and then move it into the returned pair, like so:
std::pair<int, std::string> operator()(std::string value)
{
return {count++, std::move(value)};
}
Since std::pair's constructor is not explicit, you do not even need the call to std::make_pair() in this case.

Populate vector with integer sequence, on construction

I am trying to populate a vector (or other container) with a sequence of integers on construction of the vector (contrasting to this question). The following code does what I intend, using the range constructor for a vector, and Boost's counting_range:
#include <iostream>
#include <vector>
#include <boost/range/counting_range.hpp>
using namespace std;
int main () {
vector<int> test_vector(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
for (auto i : test_vector) cout << i << endl;
}
Questions:
Can I eliminate the duplication in counting_range(2,10).begin() and counting_range(2,10).end()? Currently I'm specifying the range of (2,10) twice.
Can this be done without Boost, using just vanilla C++11 or C++0x? Edit: or C++14?
Edit:
I'd like to instantiate the vector and specify the range all in a single statement. For example in Python I could write test_vector=range(2,9). In R/Octave/Matlab one can write test_vector=2:9 or test_vector=seq(2,9,1). In this regard I'm satisfied with what I have above.
I used "2" and "10" above, but boundary of the range could be dynamic, any integers a,b in scope with a≤b. So an initialization list like {2,3,...,9} isn't desirable since it must be specified at compile-time.
The method I've used above can also be used to initialize other containers in a single statement; the following also works:
unordered_set test_set(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
It would be nice if any solution was also as 'container independent'.
I agree with #david-rodriguez-dribeas that we can adhere to Meyer's Item by initializing the vector in the constructor's body; it isn't necessary for it to be done in the constructor's initialization list. So I have struck-out this portion below.
Motivation:
I want to do this is because I want to obey Meyer's Item 4 ("Make sure objects are initialized before they are used.") in Effective C++ elsewhere in my code. For example:
class my_class {
public:
my_class()
:vec(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end()) {}
vector<int> vec;
};
Don't over do it. The simple thing to do is to default initialize the vector, reserve and initialize from the range.
Make sure objects are initialized before they are used.
That does not mean that the member must be fully initialized in the initialization list, rather than the my_class object must be fully initialized when the constructor completes.
Other than that, just for the sake of it, there are different things you can do in vanilla C++ to handle this, like creating a helper function and returning the vector by value:
std::vector<int> create_vector() {
std::vector<int> v;
// ...
return v;
}
But I would not use this (or any other alternative) to initialize a member, only if needed (the vector is const might be sufficient excuse :))
You can constructor std::vector<T> with a sequence delimited by ranges. You just need a suitable input iterator to initialize the sequence and you can define your iterator such that you can use a default constructed iterator for the end, e.g.:
class counter: public std::iterator<std::input_iterator_tag, int> {
int current;
int end;
public:
counter(): current(), end() {}
counter(int c, int e): current(c), end(e) {}
int const& operator*() const { return this->current; }
counter& operator++() { ++current; return *this; }
counter operator++(int) { counter rc(*this); ++current; return rc; }
bool operator== (counter const& other) const {
return (end - current) == (other.end - other.current);
}
bool operator!= (counter const& other) const { return !(*this == other); }
};
std::vector<int> v(counter(2, 10), counter());
The Boost way to do this is to use boost::copy_range:
auto vec = boost::copy_range<std::vector<int>>(boost::irange(0, 10));
boost::copy_range is a function template that returns an object of its first template parameter type:
template< typename SeqT, typename Range >
inline SeqT copy_range( const Range& r )
{
return SeqT( boost::begin( r ), boost::end( r ) );
}
You do have to specify the return type as a template parameter, which can violate DRY but can be avoided using AAA style, as in my example above, or using decltype in a class initializer:
class S {
public:
S() : vec(boost::copy_range<decltype(vec)>(boost::irange(0, 10)) {}
private:
const std::vector<int> vec;
};
This has never failed anyone, and it's completely vanilla, even works with C++03 (provided you backport begin, end, like I did; if not, just use compile-time array size):
int inits[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> test_vector ( begin(inits), end(inits) );
If the sequence is another kind of sequence, you only need to adapt your initialization array for it, or use a generator object, and bam! It's done.
As for avoid the duplication in (1), what's wrong if anything with the following?
const boost::counting_range init_range(2,10);
vector<int> test_vector( begin(init_range), end(init_range) );
Instead of creating boost::counting_range, you can use directly boost::counting_iterator to initialize your sequence.
#include <iostream>
#include <vector>
#include <boost/iterator/counting_iterator.hpp>
int main ()
{
std::vector<int> aVec(boost::counting_iterator<int>(0), boost::counting_iterator<int>(10));
for (int i : aVec)
{
std::cout << i << std::endl;
}
return 0;
}

Finding in a std::vector of structures

I have:
struct MyStruct
{
char* name;
int* somethingElse;
};
And I need to find in a std::vector<MyStruct*> an element (by using std::find_if) whose name is "XYZ" ... but ... the Predicate of std::find_if (if I have managed to understand it correctly) is a plain function, and it takes in a MyStruct pointer and I have no idea where I can specify the extra "XYZ" value to be used in the comparison.
So, how can I use std::find_if or this purpose? (Obviously, looking for a nice solution, not a global variable, or just walk through the list, ....)
Thanks, f
You can use a functor for this (hope I didn't get anything wrong, as I typed it in the browser):
class finder
{
const char* name;
public:
finder(const char* _name): name(_name) {}
bool operator()(MyStruct* elem) {return strcmp(elem->name, name) == 0;}
};
finder f("sample");
std::find_if(myvector.begin(), myvector.end(), f);
If you use C++11 and lambda:
std::vector<MyStruct> mystructus;
std::find_if(mystructus.begin(), mystructus.end(),
[](const MyStruct& ms){ return ms.name == std::string("XYZ"); } );
You have two options, either use functors or lamdas.
Using functors, you create a new class (or structure) whose constructor takes the string you want to search for, then it has an operator() function that is called by std::find_if:
class my_finder
{
std::string search;
public:
my_finder(const std::string& str)
: search(str)
{}
bool operator()(const MyStruct* my_struct) const
{ return search == my_struct->name; }
};
// ...
std::find_if(std::begin(...), std::end(...), my_finder("XYZ"));
The second using lambdas is less code, but requires recent version of the compiler that can handle C++11 lambdas:
std::find_if(std::begin(...), std::end(...), [](const MyStruct* my_struct)
{ return std::string("XYZ") == my_struct->name; });
The last example can even be generalized further:
using namespace std::placeholders; // For `_1` used below in `std::bind`
// Declare a "finder" function, to find your structure
auto finder = [](const MyStruct* my_struct, const std::string& to_find) {
return to_find == my_struct->name;
};
auto xyz = std::find_if(std::begin(...), std::end(...), std::bind(finder, _1, "XYZ"));
auto abc = std::find_if(std::begin(...), std::end(...), std::bind(finder, _1, "ABC"));
This way the lambda can be reused.
Predicate is anything, that can have operator () applied to it (with the expected argument(s) and returns something convertible to bool). A pointer to function is such thing, but so is an object that defines operator().
You need to provide a predicate like this:
struct Comparator
{
Comparator(const char* find) : m_find(find){}
bool operator()(MyStruct* p) const
{
return strcmp(p->name, m_find) == 0;
}
const char* m_find;
};
Then you can std::find_if like this:
vector<MyStruct*>::iterator iter = std::find_if(vec.begin(), vec.end(), Comparator("XYZ"));
if(iter != vec.end())
{
MyStruct* p = *iter;
}
Or if your compiler supports C++11 you can use lambdas and get rid of the predicate functor:
auto it = std::find_if(vec.begin(), vec.end(), [](MyStruct* p) { return strcmp(p->name, "XYZ") == 0;});