It is known that std::array::operator[]
since C++14 is constexpr
, see declaration below:
constexpr const_reference operator[]( size_type pos ) const;
However, it is also const
qualified. This causes implications if you want to use the subscript operator of a std::array
in order to assign values to your array at compile time. For example consider the following user literal:
template<typename T, int N>
struct FooLiteral {
std::array<T, N> arr;
constexpr FooLiteral() : arr {} { for(int i(0); i < N; ++i) arr[i] = T{42 + i}; }
};
The above code won't compile if you try to declare a constexpr
variable of type FooLiteral
. This is attributed to the fact that overload resolution rules qualify the non-const qualified, non-constexpr overload of the array's subscript operator as a better match. Thus the compiler complains about calling a non-constexpr
function.
I can't figure out what was the reason for the commitee to declare this overload as const
qualified for C++14, however it seems that the implication is being noticed and there's also a proposal p0107R0 to fix this in the upcomming C++17.
My natural though to overcome this for C++14 was to somehow hack the expression, in order to evoke the correct subscript operator. What I did is the following:
template<typename T, int N>
struct FooLiteral {
std::array<T, N> arr;
constexpr FooLiteral() : arr {} {
for(int i(0); i < N; ++i) {
const_cast<T&>(static_cast<const std::array<T, N>&>(arr)[i]) = T{42 + i};
}
}
};
That is I casted the array to const
reference to evoke the correct subscript operator overload and then I const_cast
the returned object of the overloaded subscript operator to T&
in order remove its const-ness and be able to assign to it.
This works fine, but I know that const_cast
should be used with caution and to be frank I have second thoughts about if this hack can cause undefined behaviour.
Intuitively, I don't think there's a problem, since this const_cast
is taking place at compile time initialization thus, I can't think of an implication that can arise at this state.
But is that so, or am I wrong and this introduce UB to the program?
Q:
Can someone justify if this is a UB or not?
As far as I can tell this is not undefined behavior. The proposal that added constexpr to
operator[]
happened before the changes that removed the implicit const from constexpr member functions. So it looks like they just added on constexpr without reflecting on the need for keeping const or not.We can see form an earlier version of Relaxing constraints on constexpr functions that it says the following about mutating literals within a constant expression:
and we can see the earlier proposal I referenced points out the
const_cast
hack and it says:No UB here, your member
arr
is non constant, you can "play" with itsconst
ness at will (well sort of, you get what I mean)If your member was a constant expression then you'd have UB, because you have already initialized in the initalizer list and post creation you are not allowed to assume it has mutable value. Do whatever metaprogramming mumbo jumbo you wish inside the initializer list.
Not a direct answer to the question but hopefully something useful.
Having been troubled by
std::array
for a while I decided to see if it can be done better using user-code only.Turns out it can: