Search code examples
c++constructoriteratorinitializer-listexplicit

Can't Define an initializer_list in cbegin


So I can do this:

for(const auto i : { 13, 42 }) cout << i << ' ';

But I can't do this:

copy_n(cbegin({ 13, 42 }), 2, ostream_iterator<int>(cout, " "));

It gives me the error:

error: no matching function for call to cbegin(<brace-enclosed initializer list>)

What is it about the for-statement that allows this but not the cbegin function?

Edit:

It appears that the problem is that my initializer_list isn't being treated as an initializer_list, because if I do this it works:

copy_n(cbegin(initializer_list<int>{ 13, 42 }), 2, ostream_iterator<int>(cout, " "));

Live Example


Solution

  • { 13, 42 } is braced initializer list. It doesn't have a type, it is just a list and it depends on how it used as how it gets treated. std::cbegin is defined like

    template< class C > 
    constexpr auto cbegin( const C& c ) -> decltype(std::begin(c));
    

    and since the braced initializer list doesn't have a type, the template type deduction fails. In the ranged based for loop we use the list different. A range based for loop expands to

    {
        init-statement
        auto && __range = range_expression ; 
        auto __begin = begin_expr ;
        auto __end = end_expr ;
        for ( ; __begin != __end; ++__begin) { 
            range_declaration = *__begin; 
            loop_statement 
        } 
    } 
    

    with auto && __range = range_expression ; becoming auto && __range = { 13, 42 }. Now auto follows template type deduction except that since the committee decide that auto should work with braced initializer list, auto will deduce { 13, 42 } to a std::initiaizer_list<int> since the list contains only ints.


    If you change the code to

    copy_n(begin({ 13, 42 }), 2, ostream_iterator<int>(cout, " "));
    

    even though std::begin is defined like cbegin and takes a template type, <initializer_list> introduces an overload that takes a std::initializer_list and that will get called instead.