大括号初始值设定项列表的扣除指南

Deduction guide for brace initializer list

提问人:Martin Kopecký 提问时间:4/10/2021 更新时间:4/10/2021 访问量:470

问:

有一个实现与在标准 C++ 库不可用的环境中使用的非常相似:std::initializer_list

template<typename T>
class initializer_list {
public:
    using value_type = T;
    using reference = const T &;
    using const_reference = const T &;
    using size_type = size_t;
    using iterator = const T *;
    using const_iterator = const T *;

private:
    iterator m_array;
    size_type m_length;

    constexpr initializer_list( const_iterator array, size_type length ) noexcept : m_array( array ), m_length( length ) {}

public:
    constexpr initializer_list( void ) noexcept : m_array( nullptr ), m_length( 0 ) {}

    /* Number of elements */
    constexpr size_type size( void ) const noexcept {
        return m_length;
    }

    /* First element */
    constexpr const_iterator begin( void ) const noexcept {
        return m_array;
    }

    /* One past the last element */
    constexpr const_iterator end( void ) const noexcept {
        return begin() + size();
    }
};

template<typename T>
constexpr const T * begin( initializer_list<T> list ) noexcept {
    return list.begin();
}

template<typename T>
constexpr const T * end( initializer_list<T> list ) noexcept {
    return list.end();
}

然后,这将在另一个类构造函数中使用:initializer_list<T>

template<typename T>
struct user {
    user( initializer_list<T> init_values ) { ... }
};

以及同时使用这两件事的意图:

user<int> sample { 1, 2, 3, 4, 5 };

显然,编译器不知道如何推断大括号初始值设定项列表的类型,以便它使用上面实现的initializer_list。我想应该实现某种推导指南来连接我的 initializer_list 实现和大括号初始值设定项列表。但我不知道如何实现这一点。

有人可以建议我如何实施所描述的扣除指南吗?

C++ C++20 器列表初始化 推导指南

评论

3赞 Ben Voigt 4/10/2021
即使它确实知道你想使用该类,编译器也不知道怎么做。 是魔术。std::intiializer_list
1赞 cigien 4/10/2021
可以编写演绎指南,但我没有看到可以处理大括号初始值设定项的构造函数。userinitializer_list
0赞 StoryTeller - Unslander Monica 4/10/2021
用户代码无法取代内置语言功能的工作方式。您需要将实现挂接到编译器中。但是,如果它有选项,它也将具有(非常轻的)标头可用。
0赞 Martin Kopecký 4/10/2021
@cigien:我的实现或多或少复制了 std::initializer_list,并且没有任何东西需要大括号初始值设定项......
0赞 chris 4/10/2021
如果你想让编译器识别这一点,它需要在 in 和 in 中,但此时你的头文件也可能是编译器附带的(独立)头文件。<initializer_list>std

答:

4赞 Nicol Bolas 4/10/2021 #1

在标准 C++ 库不可用的环境中

没有这样的事情。虽然独立的 C++ 实现可以自由地只实现标准库的一部分,但所有有效的 C++ 实现都必须提供一些组件。 就是这些组件之一。std::initializer_list

因此,如果您具有有效的 C++ 11 或更高版本的 C++ 实现,则必须具有标头及其内容。这不是可选的。如果你的实现没有提供,那么它就是有缺陷的。<initializer_list>

它不是可选的原因是,它的重要功能(即,从支撑的初始化列表生成)是 C++ 语言的函数,而不是库的函数。也就是说,编译器之外的代码不可能使语法结构成为与行为方式完全相似的类型。std::initializer_list{}std::initializer_list

请考虑您的代码:

user<int> sample { 1, 2, 3, 4, 5 };

如果你仔细想想,这应该意味着将调用一个接受 5 个参数的构造函数。毕竟,如果有一个 5 个整数参数的构造函数,这意味着什么。但这不是你想要的意思,也不是这个意思。为什么?user<int>uservector<int>

因为 C++ 的语言有一个关于列表初始化的特殊规则,该规则检测构造函数的存在,该构造函数采用与 braced-init-list 类型匹配的 a,然后创建一个传递给此构造函数。此规则抠出构造函数的存在,该构造函数采用其他类型,而没有其他类型std::initializer_liststd::initializer_liststd::initializer_list

你的代码不起作用,不是因为缺乏演绎指南,而是因为就语言而言,你的类型没有特殊的规则。initializer_list

不能使用用户定义类型重新创建此语言行为。就像你不能使 return 成为 以外的类型一样。就像你不能使具有与 相同的行为一样。typeidstd::type_infoenum class byte: unsigned char{};std::byte