I have the following code to address a n-dimensional tensor class (offset is a std::vector of std::size_t):
template <typename ...Ts>
double Tensor::at(int first, Ts... others) {
int i = 0;
std::size_t index = static_cast<std::size_t>(first)*_offset[0];
(void)std::initializer_list<int>{(index += _offset[++i]*static_cast<std::size_t>(others), 0)...};
return _data[index];
}
This works but I have some questions since I wrote this code by piecing lots of information I found online.
The first one is what exactly is happening at line 5 and the correct name for it. If I am not wrong this should unpack the expression and create a braced-init-list[*]. Each element of this list is itself a comma-separated list in the form (exp,0)[**]. The expansion should therefore be {(exp1,0),...(expn,0)}. Am I right?
The second one is about the evaluation order. [**] should be only useful to provide a return value to the constructor of the inizialier_list and not serve other purposes (even if index could be itself the return value?). [*] instead, gives an order to the evaluation of the expression. I found it in the example of a print function:
(void)std::initializer_list<int>{(print(others),0)...};
so that print is called in order on the parameters. Is this also true for composed expressions?:
(void)std::initializer_list<int>{(print(compute(others)),0)...};
so that if "compute" depends on a state, this is updated according to the order of the parameters leading to always the same results (independently of compiler ecc...). In the tensor example this refers to the ++i.
1. Your understanding is correct.
2. I'm not sure what you mean by "composed expressions", etc, but yes, initializers in braces are always evaluated left-to-right. See cppreference:
- In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.
3. This is not a fold expression. A fold expressions must have one of the following forms:
( pack op ... )
( ... op pack )
( pack op ... op init )
( init op ... op pack )
Where op
is a binary operator (most of them are allowed), pack
is an expression containing at least one unexpanded parameter pack (Ts
or others
in your case), init
is just a normal expression, and ...
must be present literally.
Your function can be simplified using a fold expression. Instead of:
(void)std::initializer_list<int>{(index += _offset[++i]*static_cast<std::size_t>(others), 0)...};
you can write:
((index += _offset[++i]*static_cast<std::size_t>(others)), ...);