Search code examples
c++c++11initializer-listargument-dependent-lookup

Why does ADL not work with an unnamed initializer_list?


Consider the following code. Here, if we use std::begin on an unnamed initializer_list with explicit std::, it works OK. If we omit the std:: and use begin on a named initializer_list, it also works fine. But if we omit std:: and do the rest similarly to the first case, it fails to compile.

#include <iostream>
#include <iterator>
void func(int len, const int* x)
{
    for(int i=0;i<len;++i)
        std::cout << x[i] << "\n";
}
int main()
{
    {
        // OK
        func(5, std::begin({1,3,6,823,-35}));
    }
    {
        // OK
        auto&& list = {1,3,6,823,-35};
        func(5, begin(list));
    }
//  {
//      // Fails to compile
//      func(5, begin({1,3,6,823,-35}));
//  }
}

I get the following compilation error (after I uncomment the faulty code):

test.cpp: In function ‘int main()’:
test.cpp:21:11: error: ‘begin’ was not declared in this scope
   func(5, begin({1,3,6,823,-35}));
           ^~~~~
test.cpp:21:11: note: suggested alternative:
In file included from /usr/include/c++/8/string:51,
                 from /usr/include/c++/8/bits/locale_classes.h:40,
                 from /usr/include/c++/8/bits/ios_base.h:41,
                 from /usr/include/c++/8/ios:42,
                 from /usr/include/c++/8/ostream:38,
                 from /usr/include/c++/8/iostream:39,
                 from test.cpp:1:
/usr/include/c++/8/bits/range_access.h:105:37: note:   ‘std::begin’
   template<typename _Tp> const _Tp* begin(const valarray<_Tp>&);
                                     ^~~~~

Why does ADL work with a named initializer_list (i.e. list in the example above) but fail with an unnamed one?


Solution

  • but fail with an unnamed one?

    No, {1,3,6,823,-35} is not an unnamed std::initializer_list. {1,3,6,823,-35} is a braced-init-list. Even it could be used to construct an std::initializer_list in specified contexts but it's not std::initializer_list itself. Then ADL won't work for begin({1,3,6,823,-35}).

    A braced-init-list is not an expression and therefore has no type, e.g. decltype({1,2}) is ill-formed.

    and

    A special exception is made for type deduction using the keyword auto , which deduces any braced-init-list as std::initializer_list.

    That's why the 2nd case works; list is deudced as std::initializer_list&&.