first I want to say that using references to Value in this example is a must.
So,I have a tree (for simplicity, this is tree with nodes that can only have one child, this code is not my original assignment, but the problem is the same) and I want to store values in it.
To export the values, i have to use vector of std::pairs, with key and reference to value.
I'm trying to do that in a recursive function 'col', to push_back the values one after another.
My problem is, the final values are different. When i switched references to pointers, it worked properly. But as I said, it has to be references.
I don't understand, whats the difference between those two in this case? They both should point to a memory on a heap and that address should stay the same, right?
Here is the code:
#include <memory>
#include <vector>
#include <iostream>
template <typename Value>
class Tree {
public:
class Node {
std::unique_ptr<Value> value;
std::unique_ptr<Node> child;
public:
friend Tree;
Node(const Value i) : value(std::make_unique<Value>(i)) {}
Value* getvalue() { return value.get();}
Node* getchild() { return child.get();}
};
const std::vector<std::pair<std::string,const Value&>> collect() {
std::vector<std::pair<std::string,const Value&>> list;
col(list, root.get());
return list;
}
void col(std::vector<std::pair<std::string,const Value&>>& list, Node* node) {
list.push_back(std::make_pair("k", *node->getvalue()));
if (node->getchild() != nullptr) {
col(list, node->getchild());
}
}
void addNode(const Value i) {
add(root.get(), i);
}
Node* getroot() { return root.get();}
private:
std::unique_ptr<Node> root = std::make_unique<Node>(0);
void add(Node* node, const Value& i) {
if (node->getchild() == nullptr) {
node->child = std::make_unique<Node>(i);
} else {
add(node->getchild(), i);
}
}
};
int main() {
Tree <int>t;
t.addNode(1);
t.addNode(2);
t.addNode(3);
auto a = t.collect();
for (auto p : a) {
std::cout << p.first << " " << p.second << "\n";
}
}
The output is:
k 0
k -424282688
k -424282688
k 0
(and is different after each call)
list.push_back(std::make_pair("k", *node->getvalue()));
std::make_pair deduces the template arguments for std::pair from its function arguments, and never uses reference types for them (note the std::decay part on the linked page). So the return type of make_pair is std::pair<const char*, Value>, instantiated as std::pair<const char*, int>. The int second; member of this pair is a copy of *node->getvalue().
vector<T>::push_back(T&&) requires an argument of the actual element type, which here is T = std::pair<std::string, const int&>. There's an implicit conversion from std::pair<const char*, int> to std::pair<std::string, const int&>: the std::string first; member is constructed from the raw string pointer, and the const int& second; member is bound to the member of the input pair.
But the std::pair<const char*, int> was a temporary, so as soon as the statement ends, the lifetime of the copied and referenced value is over. Next time you try to use the reference, bang.
Instead of using make_pair, specify the exact type you need:
list.push_back(std::pair<std::string, const Value&>("k", *node->getvalue()));
or
list.push_back(decltype(list)::value_type("k", *node->getvalue()));
or put a using OutPairType = std::pair<std::string, const Value&>; in the class definition and use that instead of typing it out elsewhere.
Also note that when a std::pair has reference members, like the default behavior of any struct with reference members, the operator= copy and move assignment operators will copy or move the referenced objects. They will not (and cannot) change the reference member to refer to the same object as the right hand side's member, like a pointer assignment would. And std::vector sometimes uses operator= of its value_type (though not in push_back). You might want to consider using std::pair<std::string,std::reference_wrapper<const Value>> instead.
Related
Say I have a class that has only constants, since the values should never change.
struct Test {
const std::string id;
const int a;
const double b;
};
Later on, I want to add objects into a vector, but I want the vector to be sorted by b from largest to smallest. I use insertion sort, since there will only ever be a small number (maybe 5).
std::vector<Test> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && itr->b > b ) ++itr;
values.insert( itr, { id, a, b } );
// end sort
}
Attempting the above, I get the following error while attempting to insert into the vector:
error: object of type 'Test' cannot be assigned because its copy assignment operator is implicitly deleted
I would like to keep with using a vector for this problem; but I can't seem to find a way around the sorting issue. The only other option I could think of was to effectively recreate the vector constantly. Alternatively, using a multi-set or similar, then once all values are added, I could dump into an array.
Is there a way around this limitation, still using a vector and not making everything non-const? Or will I be forced to change my structure, or move away into a temporary object first?
Edit: Also trying to avoid the use of pointers
As has already been pointed out in the comments, sorting requires moving around elements in your vector. And moving around elements generally requires move-assignment, which generally requires mutation of elements…
There is one way out of this dillema: instead of assigning a new value to the existing object, create a new object with the new value on top of the existing one. One can do so by defining a copy/move constructor like, e.g.:
Test& operator =(Test&& other)
{
this->~Test();
return *new (this) Test(std::move(other));
}
There is just one problem with this: via [basic.life]/8.3, we're not allowed to use any existing pointer or reference to the original object, or even just the name of the original object after such an assignment. You would always have to use the result of the assignment (the return value of placement-new) or a laundered pointer as the sole means of accessing the object going forward. Since it is not specified how std::sort operates exactly, we cannoy rely on it doing so.
What we could to is build a wrapper like this (noexcept ommited for readability):
template <typename T>
class const_element
{
T value;
const T& laundered_value() const { return *std::launder(&value); }
T& laundered_value() { return *std::launder(&value); }
public:
template <typename... Args>
explicit const_element(Args&&... args) : value { std::forward<Args>(args)... } {}
const_element(const const_element& e) : value(e.laundered_value()) {}
const_element(const_element&& e) : value(std::move(e.laundered_value())) {}
const_element& operator =(const const_element& e)
{
laundered_value().~T();
new (&value) T(e.laundered_value());
return *this;
}
const_element& operator =(const_element&& e)
{
laundered_value().~T();
new (&value) T(std::move(e.laundered_value()));
return *this;
}
~const_element()
{
laundered_value().~T();
}
operator const T&() const { return laundered_value(); }
operator T&() { return laundered_value(); }
friend bool operator <(const_element& a, const_element& b)
{
return a.laundered_value() < b.laundered_value();
}
};
What this does is wrap an object of some type T, implement copy and move assignment in the way described above, and make sure that any access to the current value always goes through a laundered pointer.
Then we can just do
std::vector<const_element<Test>> values;
values.emplace_back("b", 1, 0.0);
values.emplace_back("a", 0, 0.0);
values.emplace_back("c", 2, 0.0);
std::sort(begin(values), end(values));
working example here
All that being said, I would recommend to just not do this. If you want an object that cannot be modified, simply use a const T rather than a T that solely consists of const members. You cannot have a vector of const T. But you can have a vector of T and then just pass around a reference to a const vector or a range of const elements…
You could store pointers in the vector. Of course, you would also need to clean everything up. Example at http://cpp.sh/3h7dr
std::vector<Test*> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && ((*itr)->b > b) ) ++itr;
Test* t = new Test{id,a,b};
values.insert( itr, t );
// end sort
}
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;
Let
class A
{
std::vector<std::shared_ptr<int>> v_;
};
Now I'd like to add access to v_ using two public member functions
std::vector<std::shared_ptr<int>> const & v() { return v_; }
and
std::vector<std::shared_ptr<int const> const & v() const { TODO }
I cannot replace TODO with return v_; though.
One option would be to not return a reference but a copy. Apart from the obvious performance penalty, this would also make the interface somewhat less desirable.
Another option is to make TODO equal to return reinterpret_cast<std::vector<std::shared_ptr<int const>> const &>(v_);
My question is, is this undefined behavior? Or, alternatively, is there a better option, preferably without using reinterpret_cast?
A way to avoid copying the container is to provide transform iterators that transform the element on dereference:
#include <vector>
#include <memory>
#include <boost/iterator/transform_iterator.hpp>
class A
{
std::vector<std::shared_ptr<int> > v_;
struct Transform
{
template<class T>
std::shared_ptr<T const> operator()(std::shared_ptr<T> const& p) const {
return p;
}
};
public:
A() : v_{std::make_shared<int>(1), std::make_shared<int>(2)} {}
using Iterator = boost::transform_iterator<Transform, std::vector<std::shared_ptr<int> >::const_iterator>;
Iterator begin() const { return Iterator{v_.begin()}; }
Iterator end() const { return Iterator{v_.end()}; }
};
int main() {
A a;
// Range access.
for(auto const& x : a)
std::cout << *x << '\n';
// Indexed access.
auto iterator_to_second_element = a.begin() + 1;
std::cout << **iterator_to_second_element << '\n';
}
Putting aside the discussion of whether or not you should return a reference to a member...
std::vector already propagates its own const qualifier to the references, pointee's and iterators it returns. The only hurdle is making it propagate further to the pointee type of the std::shared_ptr. You can use a class like std::experimental::propagate_const (that will hopefully be standardized) to facilitate that. It will do as its name implies, for any pointer or pointer-like object it wraps.
class A
{
using ptr_type = std::experimental::propagate_const<std::shared_ptr<int>>;
std::vector<ptr_type> v_;
};
Thus TODO can become return v_;, and any access to the pointees (like in the range-based for you wish to support) will preserve const-ness.
Only caveat is that it's a moveable only type, so copying out an element of the vector will require a bit more work (for instance, by calling std::experimental::get_underlying) with the element type of the vector itself.
I have the following code:
#include <map>
using namespace std;
struct A {};
map</*const*/ A *, int> data;
int get_attached_value(const A *p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data) *p.first = A();
}
My problem is that this code fails on a type error both when I comment and uncomment the const in the type of data. Is there any way I can solve this without using const_cast and without losing the const in get_attached_value?
The problem seems to be in the pointee type, which has to be the same in both pointer declarations (map key type and the get_attached_value's argument).
OP's code uses const A*, which is a pointer to a const instance of class A (an alternative spelling is A const *). Leaving this const in both map declaration and in get_attached_value' argument almost works, but reset_all does not allow you to assign a new value to *p.first, because the resulting type is A const& (which cannot be assigned into).
Removing both consts works as well, but OP wants to keep a const in get_attached_value.
One solution for OP's requirements, keeping as many consts as possible, seems to be to change the pointer type to a const pointer to a non-const instance of A. This will keep reset_all working, while allowing to use a const pointer in both map declaration and get_attached_value's argument:
#include <map>
using namespace std;
struct A {};
map<A * const, int> data;
int get_attached_value(A * const p) {
return data.at(p);
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
Another possible solution, with map's key as non-const but the get_attached_value's parameter const, could use std::lower_bound with a custom comparator to replace the data.at() call:
#include <map>
#include <algorithm>
using namespace std;
struct A {};
map<A*, int> data;
int get_attached_value(A const * const p) {
auto it = std::lower_bound(data.begin(), data.end(), p,
[] (const std::pair<A* const, int>& a, A const* const b) {
return a.first < b;
}
);
return it->second;
}
void reset_all() {
for (const auto &p : data)
*p.first = A();
}
However, this solution will be significantly less efficient than one that would use map's native search functions - std::lower_bound uses linear search when input iterators are not random access.
To conclude, the most efficient solution in C++11 or lower would probably use a const pointer as the map's key, and a const_cast in the reset_all function.
A bit more reading about const notation and pointers can be found here.
I have a C++ class that acts like a container: it has size() and operator[] member functions. The values stored "in" the container are std::tuple objects. However, the container doesn't actually hold the tuples in memory; instead, it constructs them on-demand based on underlying data stored in a different form.
std::tuple<int, int, int>
MyContainer::operator[](std::size_t n) const {
// Example: draw corresponding elements from parallel arrays
return { underlying_data_a[n], underlying_data_b[n], underlying_data_c[n] };
}
Hence, the return type of operator[] is a temporary object, not a reference. (This means it's not an lvalue, so the container is read-only; that's OK.)
Now I'm writing an iterator class that can be used to traverse the tuples in this container. I'd like to model RandomAccessIterator, which depends on InputIterator, but InputIterator requires support for the expression i->m (where i is an iterator instance), and as far as I can tell, an operator-> function is required to return a pointer.
Naturally, I can't return a pointer to a temporary tuple that's constructed on-demand. One possibility that comes to mind is to put a tuple instance into the iterator as a member variable, and use it to store a copy of whichever value the iterator is currently positioned on:
class Iterator {
private:
MyContainer *container;
std::size_t current_index;
// Copy of (*container)[current_index]
std::tuple<int, int, int> current_value;
// ...
};
However, updating the stored value will require the iterator to check whether its current index is less than the container's size, so that a past-the-end iterator doesn't cause undefined behavior by accessing past the end of the underlying arrays. That adds (a small amount of) runtime overhead — not enough to make the solution impractical, of course, but it feels a little inelegant. The iterator shouldn't really need to store anything but a pointer to the container it's iterating and the current position within it.
Is there a clean, well-established way to support operator-> for iterator types that construct their values on-demand? How would other developers do this sort of thing?
(Note that I don't really need to support operator-> at all — I'm implementing the iterator mainly so that the container can be traversed with a C++11 "range for" loop, and std::tuple doesn't have any members that one would typically want to access via -> anyway. But I'd like to model the iterator concepts properly nonetheless; it feels like I'm cutting corners otherwise. Or should I just not bother?)
template<class T>
struct pseudo_ptr {
T t;
T operator*()&&{return t;}
T* operator->(){ return &t; }
};
then
struct bar { int x,y; };
struct bar_iterator:std::iterator< blah, blah >{
// ...
pseudo_ptr<bar> operator->() const { return {**this}; }
// ...
};
This relies on how -> works.
ptr->b for pointer ptr is simply (*ptr).b.
Otherwise it is defined as (ptr.operator->())->b. This evaluates recursively if operator-> does not return a pointer.
The pseudo_ptr<T> above gives you a wrapper around a copy of T.
Note, however, that lifetime extension doesn't really work. The result is fragile.
Here's an example relying on the fact that operator-> is applied repeatedly until a pointer is returned. We make Iterator::operator-> return the Contained object as a temporary. This causes the compiler to reapply operator->. We then make Contained::operator-> simply return a pointer to itself. Note that if we don't want to put operator-> in the Contained on-the-fly object, we can wrap it in a helper object that returns a pointer to the internal Contained object.
#include <cstddef>
#include <iostream>
class Contained {
public:
Contained(int a_, int b_) : a(a_), b(b_) {}
const Contained *operator->() {
return this;
}
const int a, b;
};
class MyContainer {
public:
class Iterator {
friend class MyContainer;
public:
friend bool operator!=(const Iterator &it1, const Iterator &it2) {
return it1.current_index != it2.current_index;
}
private:
Iterator(const MyContainer *c, std::size_t ind) : container(c), current_index(ind) {}
public:
Iterator &operator++() {
++current_index;
return *this;
}
// -> is reapplied, since this returns a non-pointer.
Contained operator->() {
return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
}
Contained operator*() {
return Contained(container->underlying_data_a[current_index], container->underlying_data_b[current_index]);
}
private:
const MyContainer *const container;
std::size_t current_index;
};
public:
MyContainer() {
for (int i = 0; i < 10; i++) {
underlying_data_a[i] = underlying_data_b[i] = i;
}
}
Iterator begin() const {
return Iterator(this, 0);
}
Iterator end() const {
return Iterator(this, 10);
}
private:
int underlying_data_a[10];
int underlying_data_b[10];
};
int
main() {
MyContainer c;
for (const auto &e : c) {
std::cout << e.a << ", " << e.b << std::endl;
}
}