I was toying a little bit with the indices trick to see where I could go to with and came across a strange error... First, the plain not-so-old indices:
template<std::size_t...>
struct indices {};
template<std::size_t N, std::size_t... Indices>
struct make_indices:
make_indices<N-1, N-1, Indices...>
{};
template<std::size_t... Indices>
struct make_indices<0, Indices...>:
indices<Indices...>
{};
I created a compile-time array class derived from a std::initializer_list
and had it indexable (assume that N3471 is support by your compiler. It will be in the next standard anyway). Here it is:
template<typename T>
struct array:
public std::initializer_list<T>
{
constexpr array(std::initializer_list<T> values):
std::initializer_list<T>(values)
{}
constexpr auto operator[](std::size_t n)
-> T
{
return this->begin()[n];
}
};
So, I tried to create a function that returns a copy of an array
after having added 1 to each of its members:
template<typename T, std::size_t... I>
auto constexpr add_one(const array<T>& a, indices<I...>)
-> const array<T>
{
return { (a[I]+1)... };
}
And to finish with the code, here is my main:
int main()
{
constexpr array<int> a = { 1, 2, 3 };
constexpr auto b = add_one(a, make_indices<a.size()>());
return 0;
}
I did not think that code would compile anyway, but I am quite surprised by the error message (Here is the ideone code):
In function 'int main()':
error: 'const smath::array<int>{std::initializer_list<int>{((const int*)(& const int [3]{2, 3, 4})), 3u}}' is not a constant expression
So, could someone explain to me what exactly is not constant enough for the compiler in the above code?
EDIT: Follow-ups for that question
The main problem with std::initializer_list is probably the quirks regarding uniform initialization. But this can be solved easily: add an extra layer of indirection, i.e. define your own initializer_list: This is just a wrapper over std::initializer_list .
(not to be confused with member initializer list ) An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T.
A pointer to the first element of the initializer_list. If the list is empty, the pointer is the same for the beginning and end of the list. Returns a pointer to one past the last element in an initializer list. A pointer to one past the last element in the list. If the list is empty, it's the same as the pointer to the first element in the list.
Even though the core language has been amended for std::initializer_list , the expression {1, 2, 3, 4, 5} does not have the type std::initializer_list<int> . So if you have a template function: And you want to call it with an initializer list: You’ll get an error. This makes generic make function more complicated, because that won’t compile:
From: the man himself http://www.stroustrup.com/sac10-constexpr.pdf
Specifically: its return type, and the types of its parameters (if any), are literal types (see x2.2). For concreteness, literal types include bool, int, or double; its body is a compound statement of the form { return expr; } where expr is such that if arbitrary constant expressions of appropriate types are substituted for the parameters in expr, then the resulting expression is a constant expression as defined in introductory paragraph of x2. The expression expr is called a potential constant expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With