如何使用带有std :: initializer_list的构造函数设计类?

问题描述 投票:7回答:2

[当类的构造函数重载为std::initializer_list时,即使其他构造函数重载似乎是更好的匹配,此重载也将优先。 Sutter的GotW#1,第2部分,以及Meyers的Effective Modern C++,项目7中对此问题进行了详细描述。

此问题显现在哪里的经典示例是在初始化std::vector时大括号:

std::vector<int> vec{1, 2};
// Is this a vector with elements {1, 2}, or a vector with a single element 2?

Sutter和Meyers都建议避免在类设计中使用initializer_list构造函数重载可能导致程序员产生歧义。

萨特:

指南:设计类时,请避免提供一个 使用initializer_list构造函数模棱两可地重载,因此 用户无需使用()即可找到这样的隐藏构造函数。

Meyers:

因此,最好设计您的构造函数,以便 客户端是否使用括号还是调用都不会影响调用的重载 大括号。换句话说,从现在被视为错误的内容中学习 std :: vector接口的设计,并将您的类设计为 避免它。

但是他们都没有描述如何 vector应该被设计来避免这个问题!

所以这是我的问题:应该如何设计vector以避免在initializer_list构造函数重载时避免歧义(不丢失任何功能)?

c++ c++11 vector initializer-list
2个回答
8
投票

[我将采用与piecewise_construct中的pairdefer_lock中的unique_lock所采用的标准相同的方法:在构造函数上使用标签:

struct n_copies_of_t { };
constexpr n_copies_of_t n_copies_of{};

template <typename T, typename A = std::allocator<T>>
class vector {
public:
    vector(std::initializer_list<T>);
    vector(n_copies_of_t, size_type, const T& = T(), const A& = A());
    // etc.
};

这样:

std::vector<int> v{10, 20}; // vector of 2 elems
std::vector<int> v2(10, 20); // error - not a valid ctor
std::vector<int> v3(n_copies_of, 10, 20); // 10 elements, all with value 20.

另外,我总是忘记是值20的10个元素还是值10的20个元素,因此标签有助于阐明这一点。


1
投票

为了完整起见,一种避免歧义的可能方法(不是我提倡的一种方法是使用静态工厂方法作为将initializer_list构造函数与其他方法隔离的一种方法。

例如:

template <typename T>
class Container
{
public:
    static Container with(size_t count, const T& value)
    {
        return Container(Tag{}, count, value);
    }

    Container(std::initializer_list<T> list) {/*...*/}

private:
    struct Tag{};
    Container(Tag, size_t count, const T& value) {/*...*/}
};

用法:

auto c1 = Container<int>::with(1, 2); // Container with the single element '2'
auto c2 = Container<int>{1, 2}; // Container with the elements {1, 2}

这种静态工厂方法让人联想到对象是allocated and initialized in Objective-C。嵌套的Tag结构用于确保initializer_list重载不可行。


或者,可以将initializer_list构造函数更改为静态工厂方法,该方法可以使其他构造函数重载保持完整:

template <typename T>
class Container
{
public:
    static Container with(std::initializer_list<T> list)
    {
        return Container(Tag{}, list);
    }

    Container(size_t count, const T& value) {/*...*/}

private:
    struct Tag{};
    Container(Tag, std::initializer_list<T> list) {/*...*/}
};

用法:

auto c1 = Container<int>{1, 2}; // Container with the single element '2'
auto c2 = Container<int>::with({1, 2}); // Container with the elements {1, 2}
© www.soinside.com 2019 - 2024. All rights reserved.