Move a vector<T*> to vector<const T*> - c++

Is it possible to move a vector<T*> to a vector<const T*> without copying it and without relying on reinterpret_cast<>? I.e.
vector<int*> get() {
return ...;
}
vector<const int*> getConst() {
return whatgoeshere(get());
}

I'm going to attack this from another angle. And address a possible design issue. You didn't specify what comes in the ..., but assuming get populates a vector and then returns it, the solution in my view is to lift the code that does the populating outside of both functions.
template<typename Int>
void do_get(std::vector<Int*>& v) {
// Populate v
}
auto get() {
std::vector<int*> ret;
do_get(ret);
return ret;
}
auto getConst() {
std::vector<const int*> ret;
do_get(ret);
return ret;
}
One source of truth for the populating logic. And while the two original functions are identical, it's negligible. Furthermore on a sane implementation it won't do any superfluous copies, because RVO is amazing.

No.
Although a T* may be trivially converted to a const T*, a container of T* is not "related" to a container of const T*, so there is simply no functionality to do what you ask.
Consider also that such functionality might hypothetically assign a int** to a const int**, which is not permitted (there is no special case provided for when the programmer intended this assignment to take place as part of a swap operation, as far as I know).
Furthermore, a reinterpret_cast would merely be hacking over these facts, giving your program undefined behaviour.
You are stuck with three options:
Copy the vector (O(n))
Make it so that you have the container you wanted in the first place (O(∞))
Make it so that you don't need the new container type at all (O(?))

Related

Why in particular should I rather pass a std::span than a std::vector& to a function?

I know this might overlap with the question What is a “span” and when should I use one?, but I think the answer to this specific part of the question is pretty confusing. On one hand, there are quotes like this:
Don't use it if you have a standard library container (or a Boost container etc.) which you know is the right fit for your code. It's not intended to supplant any of them.
But in the same answer, this statement occurs:
is the reasonable alternative to passing const vector& to functions when you expect your data to be contiguous in memory. No more getting scolded by high-and-mighty C++ gurus!
So what part am I not getting here? When would I do this:
void foo(const std::vector<int>& vec) {}
And when this?
void foo(std::span<int> sp) {}
Also, would this
void foo(const std::span<int> sp) {}
make any sense? I figured that it shouldn't, because a std::span is just a struct, containing a pointer and the length. But if it doesn't prevent you from changing the values of the std::vector you passed as an argument, how can it replace a const std::vector<T>&?
The equivalent of passing a std::vector<int> const& is not std::span<int> const, but rather std::span<int const>. The span itself being const or not won't really change anything, but more const is certainly good practice.
So when should you use it?
I would say that it entirely depends on the body of the function, which you omitted from your examples.
For example, I would still pass a vector around for this kind of functions:
std::vector<int> stored_vec;
void store(std::vector<int> vec) {
stored_vec = std::move(vec);
}
This function does store the vector, so it needs a vector. Here's another example:
void needs_vector(std::vector<int> const&);
void foo(std::vector<int> const& vec) {
needs_vector(vec);
}
As you can see, we need a vector. With a span you would have to create a new vector and therefore allocate.
For this kind of functions, I would pass a span:
auto array_sum(std::span<int const> const values) -> int {
auto total = int{0};
for (auto const v : values) {
total += v;
}
return total;
}
As you can see, this function don't need a vector.
Even if you need to mutate the values in the range, you can still use span:
void increment(std::span<int> const values) {
for (auto& v : values) {
++v;
}
}
For things like getter, I will tend to use a span too, in order to not expose direct references to members from the class:
struct Bar {
auto get_vec() const -> std::span<int const> {
return vec;
}
private:
std::vector<int> vec;
};
Regarding the difference between passing a &std::vector and passing a std::span, I can think of two important things:
std::span allows you to pass only the data you want the function to see or modify, as opposed to the whole vector (and you don't have to pass a start index and an end index). I've found this was much needed to keep code clean. After all, why would you give a function access to any more data than it needs?
std::span can take data from multiple types of containers (e.g. std::array, std::vector, C-style arrays).
This can of course be also done by passing C-style arrays - std::span is just a wrapper around C-style arrays with some added safety and convenience.
Another differentiator between the two: In order to modify size of the owning vector inside your function (via std::vector::assign or std::vector::clear, for instance), you would rather pass a std::vector& than a std::span, since span doesn't provide those features.
You can modify the contents of a std::span, but you can't change its size.

const correctness for containers

After years of blindly accepting the fact that std::vector<T>::operator[] const returns const_reference, but, in light of how const works for smart pointers, I'm now beginning to wonder why it and the rest of the STL containers were designed that way. It seems that the "constness" of a const std::vector is being applied both to the vector and its elements, whereas for smart pointers the "constness" only applies to the pointer and not the element to which it's pointing.
To clarify, it seems like there should be a vector-like container where const just means that a user can't change the size of the container, but the elements in the container are mutable. My main question is: Is there something that would prevent this type of container from being "const correct"?
It seems that there are a couple of hackish workarounds by adding an extra layer of indirection (e.g. std::vector<std::unique_ptr<T>> const) to accomplish this, but I'm looking for something a little less awkward in terms of maintenance.
As an aside, if smart pointers were incorporated into the language before the STL containers, would the const accessors still have been defined the way they are today?
To clarify, it seems like there should be a vector-like container where const just means that a user can't change the size of the container, but the elements in the container are mutable.
That's std::array. You set the size at compile time. For setting the size at constructor-time there's the proposed dynarray.
How about this?
#include <vector>
template<class T>
class mut_wrapper {
mutable T value;
public:
template<class... Args>
mut_wrapper(Args... args) : value(args...) {}
operator T&() const { return value; }
};
template<class T>
using mut_vector = std::vector<mut_wrapper<T>>;
void try_modify_value(const mut_vector<int> &v) {
++v[0];
}
// doesn't compile
// void try_push(const mut_vector<int> &v) {
// v.push_back(3);
// }
void try_push_mutable(mut_vector<int> &v) {
v.push_back(3);
}
The forwarding constructor and implicit conversion makes mut_wrapper<T> behave exactly like a T&, and the type alias makes mut_vector easy to use.
As required, const mut_vector<T> values can have their elements mutated but cannot have elements added or removed.
There's also no additional storage overhead or unnecessary pointer indirection, like there would be with unique_ptr.
This approach generalises to other STL containers, but shouldn't be used for keys in ordered or hashed data structures (e.g. keys of unordered_map, set) for obvious reasons.

const method in a class returning vector of pointers

Say I have this class:
#include <vector>
using namespace std;
class Bag
{
vector<int*> items;
public:
void addItem(int* i)
{
items.push_back(i);
}
const vector<int*> getItems() const
{
return items;
}
};
The problem is I want to prevent changes of the values pointed by the pointers in parameter items. But using the vector returning from getItems I'm allowed to change the pointed values.
Now, in order to fix that I can declare the parameter items as vector<const int*> but then I can't change the values in my class either.
Is there any other way to protect the values but still not using const in the items declarations?
As #KerrekSB pointed out, you need to reconstruct the structure, changing the type of the element to a const one:
std::vector<const int*> getItems() const {
return std::vector<const int*>(items.begin(), items.end());
}
Here the constructor of vector<const int*> utilizes the implicit conversion from int* to const int*. You can't just return items here, because vector<T> is invariant on T. This directly means that there's no way to convert from vector<int*> to vector<const int*> just by a type cast.
To address your comment:
Is this the only way? I feel like creating a copy is inefficient.
As for the copy, it copies only the pointer data, so typically 8 bytes per element. For a vector of pointers to larger structures, it's typically negligible, but for a vector of ints it indeed is a rather large overhead.
Which brings us to the question: why are you storing pointers in the first place? Simply store vector<int>, and then you can just return a const& to it.
If you want to avoid copies and allow read-only access to your std::vector elements, you could instead provide functions to get a const_iterator into the container:
class Bag
{
vector<int*> items;
public:
void addItem(int* i)
{
items.push_back(i);
}
//could just be called begin if that gives the correct idea
vector<int*>::const_iterator itemsBegin() const
{
return std::cbegin(items);
}
vector<int*>::const_iterator itemsEnd() const
{
return std::cend(items);
}
};
Then you would loop over the Bag contents using that iterator rather than getting out a whole vector of items. If you provided begin and end functions then you could even use range-based for-loops.

C++11 shared_pointer constness within stl containers

I have the following problem and I wonder whether there's a better way to solve it:
class myObj {
public:
typedef std::shared_ptr<myObj> handle;
typedef std::shared_ptr<const myObj> const_handle;
int someMethod() { ... }
int someConstMethod() const { ... }
};
Now what I need is a container class that somehow allows you to modify or read a collection of myObj depending on its own constness, like so:
class myCollection {
public:
typedef std::list<myObj::handle> objList;
typedef std::list<myObj::const_handle> const_objList;
inline objList& modify() { return _obl; }
// it would be nice to do this, but it won't compile as
// objList and const_objList are completely different types
inline const_objList& read() const { return _obl; } // doh! compile error...
// returning a const objList won't help either as it would return non-const
// handles, obviously.
// so I am forced to do this, which sucks as i have to create a new list and copy
void read(const_objList &l) {
std::for_each(
_obl.begin(),
_obl.end(),
[&l] (myObj::handle &h) { l.push_back(h); }
// ok as handle can be cast to const_handle
); // for_each
}
private:
objList _obl;
};
So this solution actually works as a const myCollection would only allow you to get a list of const_handle which only allows you to call non-modifying methods of myObj (GOOD).
The problem is that the "read" method is really ugly (BAD).
Another method would be to expose somehow the list methods and return const_handle and handle as needed but it's a lot of overhead, especially if you want to use something more complex than a list.
Any idea?
A list-of-pointers-to-T is not a list-of-pointers-to-constant-T.
std::list<std::shared_ptr<int>> a;
std::list<std::shared_ptr<const int>>& ra = a; // illegal but imagine it's not
std::shared_ptr<const int> x = std::make_shared<const int>(42);
ra.push_back(x); // totally legal, right?
++**a.begin(); // oops... just incremented a const int
Now a list-of-pointers-to-T is, conceptually, a constant-list-of-constant-pointers-to-constant-T, but std::list<std::shared_ptr<T>> does not support such a deep const propagation. const std::list<std::shared_ptr<T>> contains constant pointers to non-constant objects.
You can write your own variant of list<> or your own variant of shared_ptr<> that have such support. It probably won't be very easy though. A const_propagating_shared_ptr is probably the easier of the two. It would have to encapsulate an std::shared_ptr<T> object and forward almost everything to it as-is. As opposed to std::shared_ptr<T> it would have separate const and non-const versions of operator->, operator*() and get().
Given what you stated that you want to accomplish, I don't think that your solution is too bad. Imagine that some other code may be modifying the internal collection, like adding or removing values. Returning a copy of the current state of the collection is safe for client code, since it can work on the copy, without the danger of element being deleted in the meantime. But I digress, this is getting into threading issues and may not be relevant.
You could use prettier:
inline const_objList read() const
{
const_objList cl(_obl.begin(), _obl.end());
return cl;
}
However, I do think that your problems derive from mixing two types of constness: constness of the members of the collection versus the constness of the collection itself.
Instead of Modify and Read methods, that deal with the list as a whole, I would try exposing const and non-const iterators to internal list, through corresponding const and non-const methods returning said iterators.
But this immediately begs the question: why then have myCollection in the first place?
Creating entirely new collection type around std::list doesn't seem needed, unless you get a lot of proverbial bang for the buck from other, added functionality that is not visible in your sample.
You can then make your added functionality free methods that take std::list of your handles as the input. Not everything requires an object and operations on objects need not necessarily be member methods, unless access to private data is required.
You mentioned maybe using another container instead of the list. But your class, as is, won't do it, unless you have a template, where template parameter can be one of STL containers.
Which then implies that you should expose iterators.
Namely, if you foresee changing the internal collection type, you would want to make the public interface to myCollection transparent regarding the collection type. Otherwise, clients will have to recompile each time you change your mind about the internal implementation.
EDIT -----
Finally, if implementing iterators (while interesting and most correct) is too much, why not go for simple getters like in this SO post:
smart pointer const correctness
I'll quote the topmost answer by Rüdiger Stevens (it assumes vector instead of list):
template <typename T>
class MyExample
{
private:
vector<shared_ptr<T> > data;
public:
shared_ptr<const T> get(int idx) const
{
return data[idx];
}
shared_ptr<T> get(int idx)
{
return data[idx];
}
void add(shared_ptr<T> value)
{
data.push_back(value);
}
};

fix (lock) size of std::vector

Is there a way of fixing the size of a vector and still changing the contents?
I have tried making a const vector const std::vector<int> vec(10); but that prevents me from changing the values.
vec[3] = 3; gives a compiler error: assignment of read-only location.
I have also tried with a const reference to a non-const vector
std::vector<int> vec(10);
const std::vector<int>& vecref(vec);
which gives the same compiler error.
I want to be able to fix the vector size either on declaration or after an initialisation stage. I could use an old fashioned array, but I want to be able to use the vector algorithms.
I'm using g++ if that makes any difference.
With C++0x, you can use std::array<>, which is like a good old array, with the added benefit of being an STL container, therefore allowing many std::algorithms.
Alternatively, you may want to try boost::array.
boost::array
std::array
Note that there is also std::tr1::array<>.
edit:
Actually, one of the cases that I hadn't gone into was to grow the vector while reading configuration files and then fix the size after that - DanS
Then, why not this (illustrational):
#include <vector>
int main () {
std::vector<int> foo;
/* ... crunch upon foo ... */
// make a copy vector->vector:
const std::vector<int> bar (foo);
// make a copy any->vector
const std::vector<int> frob (foo.begin(), foo.end());
}
Alternatively, if you need reset() semantics, but want to forbid resize() et al, you could write a container adapter:
template <typename T, typename Allocator = allocator<T> >
class resettable_array {
public:
// container ...
typedef typename std::vector<T,Allocator>::iterator iterator;
typedef typename std::vector<T,Allocator>::const_iterator const_iterator;
...
iterator begin() { return vector_.begin() }
const_iterator begin() const { return vector_.begin(); }
...
void push_back (T const &v) { vector_.push_back (v); }
...
// custom
void reset () { ... }
private:
std::vector<T,Allocator> vector_;
};
See also:
std::vector constructors
Embed it in an object that provides only the operations that you want to allow.
Cheers & hth.,
You can make a const vector of pointers, and change the objects they point to. Not saying this is the right answer, just that it's possible.
Take a look at boost.array, it gives you a fixed size array with vector semantics (with the exception of anything that would change the size of the array).