初始化列表、参数包扩展、折叠表达式和求值顺序
Initializer list, parameter pack expansion, fold expressions and order of evaluation
我有以下代码来处理 n 维张量 class(偏移量是 std::size_t 的 std::vector):
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];
}
这行得通,但我有一些问题,因为我通过拼凑网上找到的大量信息编写了这段代码。
第一个是第 5 行发生的事情及其正确名称。如果我没记错的话,这应该解压表达式并创建一个 braced-init-list[*]。此列表的每个元素本身都是一个以逗号分隔的列表,格式为 (exp,0)[**]。因此,扩展应该是 {(exp1,0),...(expn,0)}。我说得对吗?
第二个是关于评价顺序的。 [**] 应该只用于向 inizialier_list 的构造函数提供 return 值,而不用于其他目的(即使索引本身可能是 return 值?)。 [*] 相反,给出了表达式评估的顺序。我在打印函数的例子中找到了它:
(void)std::initializer_list<int>{(print(others),0)...};
以便在参数上按顺序调用打印。对于组合表达式也是如此吗?:
(void)std::initializer_list<int>{(print(compute(others)),0)...};
因此,如果“计算”依赖于状态,则会根据导致始终相同结果的参数顺序进行更新(独立于编译器 ecc...)。在张量示例中,这是指 ++i.
- 最后一题参考了this,其中+=的用法在tensors中的用法称为fold expression。我认为情况并非如此。我错了吗?
1。你的理解是正确的。
2。我不确定你所说的“组合表达式”等是什么意思,但是是的,大括号中的初始值设定项总是被计算 left-to-right。见 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。这不是折叠表达式。 fold expressions 必须具有以下形式之一:
( pack op ... )
( ... op pack )
( pack op ... op init )
( init op ... op pack )
其中op
是一个二元运算符(大部分是允许的),pack
是一个包含至少一个未扩展参数包的表达式(Ts
或others
在您的情况下),init
只是一个普通表达式,并且 ...
必须按字面意思存在。
可以使用折叠表达式简化您的函数。而不是:
(void)std::initializer_list<int>{(index += _offset[++i]*static_cast<std::size_t>(others), 0)...};
你可以写:
((index += _offset[++i]*static_cast<std::size_t>(others)), ...);
我有以下代码来处理 n 维张量 class(偏移量是 std::size_t 的 std::vector):
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];
}
这行得通,但我有一些问题,因为我通过拼凑网上找到的大量信息编写了这段代码。
第一个是第 5 行发生的事情及其正确名称。如果我没记错的话,这应该解压表达式并创建一个 braced-init-list[*]。此列表的每个元素本身都是一个以逗号分隔的列表,格式为 (exp,0)[**]。因此,扩展应该是 {(exp1,0),...(expn,0)}。我说得对吗?
第二个是关于评价顺序的。 [**] 应该只用于向 inizialier_list 的构造函数提供 return 值,而不用于其他目的(即使索引本身可能是 return 值?)。 [*] 相反,给出了表达式评估的顺序。我在打印函数的例子中找到了它:
(void)std::initializer_list<int>{(print(others),0)...};
以便在参数上按顺序调用打印。对于组合表达式也是如此吗?:
(void)std::initializer_list<int>{(print(compute(others)),0)...};
因此,如果“计算”依赖于状态,则会根据导致始终相同结果的参数顺序进行更新(独立于编译器 ecc...)。在张量示例中,这是指 ++i.
- 最后一题参考了this,其中+=的用法在tensors中的用法称为fold expression。我认为情况并非如此。我错了吗?
1。你的理解是正确的。
2。我不确定你所说的“组合表达式”等是什么意思,但是是的,大括号中的初始值设定项总是被计算 left-to-right。见 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。这不是折叠表达式。 fold expressions 必须具有以下形式之一:
( pack op ... )
( ... op pack )
( pack op ... op init )
( init op ... op pack )
其中op
是一个二元运算符(大部分是允许的),pack
是一个包含至少一个未扩展参数包的表达式(Ts
或others
在您的情况下),init
只是一个普通表达式,并且 ...
必须按字面意思存在。
可以使用折叠表达式简化您的函数。而不是:
(void)std::initializer_list<int>{(index += _offset[++i]*static_cast<std::size_t>(others), 0)...};
你可以写:
((index += _offset[++i]*static_cast<std::size_t>(others)), ...);