Search code examples
c++c++20initializer-listlist-initializationdeduction-guide

Deduction guide for brace initializer list


There is a implementation quite similar to std::initializer_list used in an environment where standart C++ library is not available:

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();
}

Then such initializer_list<T> is about to be used in another class constructor:

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

and the intension to use both things together:

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

Obviously, the compiler does not know how to deduce the type of the brace initializer list so that it uses initializer_list as implemented above. I suppose some kind of deduction guide shall be implemented to connect my implementation of initializer_list and the brace initializer list. But I have no clue how to implement such.

Can someone advise me how to implement the described deduction guide?


Solution

  • in an environment where standart C++ library is not available

    There is no such thing. While freestanding C++ implementations are free to only implement parts of the standard library, there are some components which all valid C++ implementations must provide. std::initializer_list is among these components.

    As such, if you have a valid C++11 or higher implementation of C++, then you must have the <initializer_list> header and its contents. This is not optional. If your implementation doesn't provide one, then it is defective.

    The reason it is not optional is that the important functionality of std::initializer_list (that is, its generation from a braced-init-list) is a function of the C++ language, not of the library. That is, it is impossible for code outside of the compiler to make the {} grammatical construct become a type that is exactly analogous to how std::initializer_list behaves.

    Consider your code:

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

    If you think about it, this ought to mean that a constructor of user<int> will be called that takes 5 parameters. That's what it would mean if user had a constructor of 5 integer parameters, after all. But that's not what you want it to mean, and it wouldn't mean that for vector<int>. Why?

    Because C++'s language has a special rule about list initialization that detects the presence of a constructor which takes a std::initializer_list that matches the braced-init-list types, and then creates a std::initializer_list to pass to this constructor. This rule keys off of the presences of a constructor that takes std::initializer_list and no other type.

    Your code does not work, not because of a lack of deduction guides, but because your initializer_list type has no special rules as far as the language is concerned.

    You cannot recreate this language behavior with a user-defined type. Just as you cannot make typeid return a type other than std::type_info. Just as you cannot make enum class byte: unsigned char{}; have the same behavior as std::byte.