I want to create a std::array
of pointers without declaring the type to which the pointers point to as const
, so that I can change the values pointed to by the pointers of the array through dereference of these pointers.
So I don't want to do this:
#include <array>
int main()
{
int a = 5;
int b = 10;
std::array<const int*, 2> arr = { &a, &b }; // with const
*(arr[0]) = 20; // Because this is not possible.
}
But this:
#include <array>
int main()
{
int a = 5;
int b = 10;
std::array<int*, 2> arr = { &a, &b }; // without const
*(arr[0]) = 20; // Because now it is possible.
}
Now I want to pass this array to a function in such a way that this function can not change the values pointed to by the pointers of the array through dereference of these pointers:
#include <array>
void test(const std::array<int*, 2>& arr)
{
*(arr[0]) = 20; // I want this to not be possible (in this example it is)
}
int main()
{
int a = 5;
int b = 10;
std::array<int*, 2> arr = { &a, &b };
test(arr);
}
How can I do this? Since it is possible with C arrays:
void test(int const * const * const arr)
{
*(arr[0]) = 20; // this is not possible
}
int main()
{
int a = 5;
int b = 10;
int* arr[2] = {&a, &b};
test(arr);
}
I figured it should be possible with C++ std arrays too.
Help much appreciated. Thank you.
What you're asking for is unfortunately not possible.
The underlying issue in this case is that const only propagates on the top-level.
So making a pointer const only makes the pointer itself const but not the pointed-to object.
This is true for any class containing pointers, not just std::array
: godbolt
std::array<int*, 2> arr = { &a, &b };
auto const& arrref = arr;
static_assert(std::same_as<decltype(arrref[0]), int* const&>); // not int const * const&!
struct Foo {
int* ptr;
auto& get() const { return ptr; }
};
Foo foo{&a};
auto const& fooref = foo;
static_assert(std::same_as<decltype(fooref.get()), int* const&>); // not int const * const&!
What you would need for this to work is a pointer type that propagates its constness to the pointed-to type.
std::propagate_const
(which unfortunately is still experimental as part of the library fundamentals TS) does just that: it wraps a pointer-like type so that it does propagate const to the pointed to object.
Example: godbolt
using std::experimental::propagate_const;
void test(std::array<propagate_const<int*>, 2> const& arr)
{
// ill-formed:
//*arr[0] = 20;
}
int main()
{
int a = 5;
int b = 10;
std::array<propagate_const<int*>, 2> arr = {&a, &b};
test(arr);
// well-formed
*arr[0] = 42;
return 0;
}
Another option that works with C++20 would be to use std::span
.
std::span
is essentially just a pointer to the array, so you can add as much const to the element type as you want (just like in your c-array example where you decayed the array to a pointer to add constness)
Example: godbolt
void test(std::span<int const* const> arr)
{
// ill-formed:
//*arr[0] = 20;
}
int main()
{
int a = 5;
int b = 10;
std::array<int*, 2> arr = {&a, &b};
test(arr);
// well-formed
*arr[0] = 42;
return 0;
}