Pack Indexing and Constexpr Indices #519
Replies: 2 comments 20 replies
-
|
Because there's a major difference. Your parameter packs are not forced to be homogeneous, the types can differ from one parameter to the next. Putting them in an array forces them to all be the same type, and could also confuse the optimizer into including unused values in the final generated binary depending on how well it can see through what you're doing. |
Beta Was this translation helpful? Give feedback.
-
This is a funny sentence to me, since it not being an array is the reason it requires a constant expression.
The entire C++ language is built around static typing though. You can say that in dynamically typed languages like Python, but in C++ if you don't know the type or size of something at compile time your options are extremely limited and you have to employ workarounds like what you've shown. Having the compiler automatically transform code in various ways to make the workarounds less needed is a complicated thing to get working well, and often requires brand new syntax, as seen with the
I agree, I am not sure where this talk about array limitations came from, arrays are just a convenient way to coerce the possibly different types into a single uniform type to make working with the elements easier. If you can't coerce things into a single uniform type, then you can't use an array, but you can still use pack indexing. It just still requires a constant expression due to the language being statically typed.
How? The function is still a template context, it doesn't know what the count or types of the parameters are until it specifically checks.
That "if the index came from outside the scope" comment is the entire problem that I have been trying to get at throughout this whole conversation. C++ has no system by which variables can be tagged as having been tainted by outside context for the purpose of distinguishing what is or isn't allowed for your proposed syntax. It would have to be worked out and standardized and implemented by compilers, just to end up needing to be expanded later.
For this particular example, you can just use fold expressions: int add(auto... inputs){
return (0 + ... + inputs);
}Fold expressions already support most use cases of parameter packs, including running a block of code for each parameter in the pack: void print(auto... inputs){
([](auto input){
std::println("{}", input);
}(inputs), ...);
}The syntax isn't as familiar and straightforward as a for loop, but it is available and working now. What doesn't work well with fold expressions is when you need to deal with more than one element of the pack at once, that's when pack indexing has to be used: #include <print>
#include <type_traits>
struct PrintOption
{
bool q;
};
template<std::size_t Iterations>
void foreach(auto f)
{
[&]<std::size_t... I>(std::index_sequence<I...> const){
((f(std::integral_constant<std::size_t, I>{})), ...);
}(std::make_index_sequence<Iterations>{});
}
void print(auto... inputs){
foreach<sizeof...(inputs)>([&]<std::size_t I>(std::integral_constant<std::size_t, I> const){
if constexpr(!std::is_same_v<std::remove_cvref_t<decltype(inputs...[I])>, PrintOption>){
if constexpr(I > 0){
if constexpr(std::is_same_v<std::remove_cvref_t<decltype(inputs...[I - 1])>, PrintOption>){
if(inputs...[I - 1].q){
std::println("{:?}", inputs...[I]);
return;
}
}
}
std::println("{}", inputs...[I]);
}
});
}
int main()
{
print(3, "a", PrintOption{.q = true}, "b", 3.14);
}Output: https://compiler-explorer.com/z/bxqhrodfY 3
a
"b"
3.14Sure, you can find alternative ways to support this sort of thing without pack indexing, but it limits your options. Pack indexing lets us reach back or forward in the pack and just do what we want without workarounds. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I've been exploring pack indexing in C++26, and was curious about why the index must be a constant expression (not even a
constvalue is accepted). If you write a variadic/recursive statement, load the pack into an array, or otherwise use a more verbose (and possibly expensive) workaround, you can get the exact same functionality. Parameter packs are just that- parameters, so why can't we access members of the pack more easily?EDIT: Added a fourth and fith methods. The fourth is probably the simplest and most performant under general circumstances.
Please see comments below for further examples, including a variable-type version with compile-time type information.
Here's some example code of variadic addition functions:
if constexprto estimate the typical size, only using a buffer for larger packs.(tested with GCC (trunk), in both -O0 and -O3.)
Beta Was this translation helpful? Give feedback.
All reactions