简体   繁体   中英

Disable the std::initializer_list constructor

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.

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)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM