3

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.

2 Answers 2

4

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)

Sign up to request clarification or add additional context in comments.

Comments

4

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 }
{}

LIVE

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.