Consider a constructor accepting a parameter pack, such as
template<typename First, typename... Rest>
consteval explicit foo(const First& first, const Rest... rest)
: arrayMember{first, rest...}
{
}
where First and Rest... all have arithmetic types,
And another, different constructor that takes two arithmetic types:
explicit foo(std::size_t low, std::size_t high) { /* ... */ }
Imagine now that we wish to call the second constructor taking the two arithmetic types parameters:
foo f(3,4);
... But this actually calls the constructor that takes a parameter pack.
I am wondering whether there is a way to disambiguate the calls.
For example, is there a way (through preprocessor magics or something) to call the parameter pack constructor with brace initialization ({ ... }
), and to call the other constructor with direct initialization ((..., ...)
)?
I have thought of disabling the parameter pack constructor if the passed arguments are 2, but what if I actually wanted to call it with two parameters?
Minimal reproducible example: https://godbolt.org/z/5P9cEdf7v
CodePudding user response:
Assuming an implementation similar to this:
template <typename T, std::size_t Size>
class foo {
private:
T _vector[Size]{};
public:
using size_type = std::size_t;
// .. other constructors ..
explicit foo(size_type lower, size_type higher) { // (1)
// ...
}
template <typename Stream>
constexpr void print(Stream& stream) const {
for (auto x : _vector) stream << x << ' ';
stream << '\n';
}
};
You can replace your first constructor by one that takes a const
reference to an array, T const (&)[Size]
:
consteval explicit foo(T const (&in)[Size]) // (2)
: foo(in, std::make_index_sequence<Size>{}) {}
This constructor delegates the initialization of _vector
to another constructor which exploits the inices trick:
template <std::size_t... Is>
consteval explicit foo(T const (&in)[Size], std::index_sequence<Is...>) // (3)
: _vector{in[Is]...} {}
With this implementation:
#include <iostream>
int main() {
foo<int, 3> vec0(3, 4); // calls (1)
vec0.print(std::cout);
constexpr foo<int, 3> vec1({3, 4}); // calls (2), which calls (3)
vec1.print(std::cout);
constexpr foo<int, 3> vec2{{3, 4}}; // calls (2), which calls (3)
vec2.print(std::cout);
}