我可以依靠空参数包来正确扩展吗?
Can I rely on empty parameter packs to be expanded properly?
下面的代码定义了函数subst_first
,它将一个整数数组的第一个元素替换为另一个数组的内容。它在 gcc 和 clang 下工作(live demo). For cases //1
, //2
and //3
one of the generated index_sequence
's 为空。因此 //4
处的参数包之一具有零元素。这让我感到不安。我可以依靠这种行为来符合标准吗?
template<size_t n, size_t ... S1, size_t ... S2>
constexpr
std::array<int,3>
subst_first_impl( std::array<int,n> const &v1,
std::array<int,3> const &v2,
size_t min_n,
std::index_sequence<S1...>,
std::index_sequence<S2...> )
{ return std::array<int,3>{{ v1[S1]..., v2[min_n+S2]... }}; } // 4
template<size_t n>
constexpr
std::array<int,3>
subst_first( std::array<int,n> const &v1,
std::array<int,3> const &v2 )
{ auto const min_n= std::min( size_t(3), n );
return subst_first_impl( v1, v2, min_n,
std::make_index_sequence< min_n >(),
std::make_index_sequence< size_t(3) - min_n >() );
}
int main(){
constexpr std::array<int,3> a1{{1,2,3}};
constexpr std::array<int,2> b1{{4,5}};
constexpr std::array<int,3> b2{{6,7,8}};
constexpr std::array<int,4> b3{{9,10,11,12}};
constexpr std::array<int,0> b4{};
constexpr auto b1a1= subst_first( b1, a1 );
// ==> 4, 5, 3
constexpr auto b2a1= subst_first( b2, a1 ); // 1
// ==> 6, 7, 8
constexpr auto b3a1= subst_first( b3, a1 ); // 2
// ==> 9, 10, 11
constexpr auto b4a1= subst_first( b4, a1 ); // 3
// ==> 1, 2, 3
}
注意:我不是在寻找替换数组元素的解决方案。我对 index_sequence
和参数包的行为很感兴趣。
是的,绝对是。引用标准 [temp.variadic]:
- A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list. The form of the pattern depends on the context in
which the expansion occurs.
因此标准适应大小为零的包的扩展。
首先,std::make_index_sequence<0>
完全有效 (§20.5.3 [intseq.make]):
[ Note: make_integer_-sequence<int, 0> denotes the type integer_sequence<int> —end note ]
所以在你的情况下,你会得到 std::index_sequence<size_t>
。
并且根据 §14.5.3/7 [temp.variadic],长度为 0 的包扩展的实例化是完全有效的:
The instantiation of a pack expansion that is neither a sizeof...
expression nor a fold-expression produces a list E1, E2, ..., EN, where N is the number of elements in the pack expansion parameters. [...]
All of the Ei become elements in the enclosing list. [...] When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct, even in cases where omitting the list entirely would otherwise be ill-formed or would result in an ambiguity in the grammar.
下面的代码定义了函数subst_first
,它将一个整数数组的第一个元素替换为另一个数组的内容。它在 gcc 和 clang 下工作(live demo). For cases //1
, //2
and //3
one of the generated index_sequence
's 为空。因此 //4
处的参数包之一具有零元素。这让我感到不安。我可以依靠这种行为来符合标准吗?
template<size_t n, size_t ... S1, size_t ... S2>
constexpr
std::array<int,3>
subst_first_impl( std::array<int,n> const &v1,
std::array<int,3> const &v2,
size_t min_n,
std::index_sequence<S1...>,
std::index_sequence<S2...> )
{ return std::array<int,3>{{ v1[S1]..., v2[min_n+S2]... }}; } // 4
template<size_t n>
constexpr
std::array<int,3>
subst_first( std::array<int,n> const &v1,
std::array<int,3> const &v2 )
{ auto const min_n= std::min( size_t(3), n );
return subst_first_impl( v1, v2, min_n,
std::make_index_sequence< min_n >(),
std::make_index_sequence< size_t(3) - min_n >() );
}
int main(){
constexpr std::array<int,3> a1{{1,2,3}};
constexpr std::array<int,2> b1{{4,5}};
constexpr std::array<int,3> b2{{6,7,8}};
constexpr std::array<int,4> b3{{9,10,11,12}};
constexpr std::array<int,0> b4{};
constexpr auto b1a1= subst_first( b1, a1 );
// ==> 4, 5, 3
constexpr auto b2a1= subst_first( b2, a1 ); // 1
// ==> 6, 7, 8
constexpr auto b3a1= subst_first( b3, a1 ); // 2
// ==> 9, 10, 11
constexpr auto b4a1= subst_first( b4, a1 ); // 3
// ==> 1, 2, 3
}
注意:我不是在寻找替换数组元素的解决方案。我对 index_sequence
和参数包的行为很感兴趣。
是的,绝对是。引用标准 [temp.variadic]:
- A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list. The form of the pattern depends on the context in which the expansion occurs.
因此标准适应大小为零的包的扩展。
首先,std::make_index_sequence<0>
完全有效 (§20.5.3 [intseq.make]):
[ Note: make_integer_-sequence<int, 0> denotes the type integer_sequence<int> —end note ]
所以在你的情况下,你会得到 std::index_sequence<size_t>
。
并且根据 §14.5.3/7 [temp.variadic],长度为 0 的包扩展的实例化是完全有效的:
The instantiation of a pack expansion that is neither a
sizeof...
expression nor a fold-expression produces a list E1, E2, ..., EN, where N is the number of elements in the pack expansion parameters. [...]All of the Ei become elements in the enclosing list. [...] When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct, even in cases where omitting the list entirely would otherwise be ill-formed or would result in an ambiguity in the grammar.