Reputation: 183
I am trying to work out if it is possible to disable the std::initializer_list constructor in certain circumstances. I am writing a custom vector class that support expressions (math and relational operators). The relational expression implicitly converts to bool to allow use in an if-statement but this is causing me problems with the constructors for my vector class (v5) in some cases where I want a vector result from the relational expression.
#include <vector>
struct expr
{
std::size_t size() const { return 1; }
auto at(std::size_t i) const { return 1.0; }
operator bool() const { return true; }
};
template<typename T>
struct my_vec
{
my_vec(std::initializer_list<T> init)
: vec_{ init }
{}
my_vec(std::size_t sz)
: vec_( sz )
{}
my_vec(expr e)
: vec_( e.size() )
{
for (std::size_t i = 0; i != e.size(); ++i) vec_.at(i) = e.at(i);
}
std::vector<T> vec_;
};
void test_init_a(void)
{
// initialize_list constructor
my_vec<double> v1{ 1.0 };
my_vec<double> v2{ 1.0, 2.0 };
// size_t constructor
my_vec<double> v3(5);
// expression constructors
expr e;
my_vec<double> v4( e );
my_vec<double> v5{ e }; // <-- this one attempts the initializer_list constructor because of the implicit cast to bool and fails due to narrowing
}
I could just always use the parenthesis to construct my vector from an expression (v4) but I wanted to figure out it it is possible to disable an initializer_list constructor at all?
I have tried is to implement a wrapper to the initializer_list and use double brace initialization, however this then causes single-element access a problem (v1) as it tries to use the size_t constructor instead of the initializer_list constructor.
Upvotes: 2
Views: 562
Reputation: 172894
As a workaround, you can make the constructor template, then apply SFINAE to make it unusable when the type in std::initializer_list
is not same as the template parameter T
of the class.
template <typename X, std::enable_if_t<std::is_same_v<X, T>>* = nullptr>
my_vec(std::initializer_list<X> init)
: vec_{ init }
{}
Upvotes: 3
Reputation: 180490
I wanted to figure out it it is possible to disable an initializer_list constructor at all?
You can't. This is how the language works and if you used braced initialization and you have a std::initializer_list
constructor then that is one that is called.
What you can do though is remove the implicit conversion that is allowing the std::initializer_list
to be created in the first place. If you make expr::operator bool()
explicit
then e
can't be converted to bool
which means the only suitable overload is now my_vec(expr e)
Upvotes: 4