Is there any way to do initializer list folding as opposed to using a parameter pack? My issue is that I have a heavily overloaded constructor, and I want to invoke different constructors based on whether I use {}
or not. This seems to work fine with a initializer list, which manages to hide my other one argument constructor when I use {}
as opposed to when I just construct it with ()
, but fails if I use a parameter pack which doesn't hide my other one argument constructor.
Also on a side note, I've seen people append void in their folding expressions which I couldn't make any sense of when I referred to cppreference, nor does it seem to make any difference in my program.
Edit: As requested, an example to illustrate the problem:
#include <iostream>
#define USE_PARAMETER_PACK false
template<typename T>
struct Mega
{
int d;
T* arr;
Mega(int d) : d(d), arr(new T[d]) {}
Mega(int d, T u) : d(d), arr(new T[d])
{
std::fill(arr, arr + d, static_cast<T>(u));
}
#if USE_PARAMETER_PACK == true
template<typename ...Ts>
Mega(Ts&& ... vals) : d(sizeof...(Ts)), arr(new T[sizeof...(Ts)])
{
// fills the array with the arguments at compile time
int i = 0;
(void(arr[i++] = static_cast<T>(vals)), ...);
}
#else
template<typename U>
Mega(const std::initializer_list<U>& list) : d(list.size()), arr(new T[d])
{
auto it = list.begin();
//int i = 0;
//((arr[i++] = (list)), ...);
for (size_t i = 0; i < d; ++i, ++it)
arr[i] = static_cast<T>(*it);
}
#endif
};
template<typename T>
std::ostream& operator<<(std::ostream& os, const Mega<T>& m)
{
for (size_t i = 0; i < m.d; ++i)
os << m.arr[i] << "\t";
return os;
}
int main()
{
int* k;
k = new int[2];
k[0] = 2;
k[1] = 3;
Mega<int> l( k[0] );
// hides 1 argument ctor through {} invocation if using initializer_list,
// not so with parameter pack
Mega<int> m({ k[0]});
Mega<int> n(k[0], k[1]);
// hides 2 argument ctor through {} invocation if using initializer list
// not so with parameter pack
Mega<int> o({ k[0], k[1] });
std::cout << l << "\n";
std::cout << m << "\n";
std::cout << n << "\n";
std::cout << o << "\n";
return 0;
}
Note the commented out part, I would've liked to be able to do something like this in order for the filling out process of known size parameter lists to be figured out at compile time, rather than me using a for loop.
Should print out some garbage values for the first cout, and 2 for the second (at least in MSVC2017 it does, no idea whether this hiding mechanism conforms to the standard). Note that if you set the define to true you can use the parameter pack ctor, but it fails to hide the one argument ctor even with {}
syntax.
Edit2: Further updated the code for maximum convenience, just change the define to true in order to see that the parameter pack fails to hide the 1 and 2 argument constructors with {}
syntax, whereas the initializer list ctor manages to.
Links: Using initializer list: http://coliru.stacked-crooked.com/a/7b876e1dfbb18d73 Output:
0 0
2
3 3
2 3
Using parameter pack: http://coliru.stacked-crooked.com/a/11042b2fc45b5259 Output:
0 0
0 0
3 3
3 3
Because the length of an initializer_list
is not known at compile time (in particular, different call sites with different numbers of elements invoke the same function), it’s impossible to do any constexpr
processing on it. (The relevant member functions are constexpr
, but you can’t use them as such on a function parameter.) A fold expression in particular requires a parameter pack, whose size is always a constant expression.