4

I'm trying to use an std::initializer_list as an argument in a function that uses argument-dependent lookup (ADL). But I don't get it to work and I don't understand why. The following is a minimal failing example:

    #include <initializer_list>
    #include <iostream>

    class Foo {
    public:
      inline friend void bar(std::initializer_list<Foo> v) {
        std::cout << "size = " << v.size() << std::endl;
      }
    };

    void baz(std::initializer_list<Foo> v) {
      std::cout << "size = " << v.size() << std::endl;
    }

    int main(){
      Foo a;
      //bar({a,a});   // error: use of undeclared identifier 'bar'
      baz({a,a});   // works

      return 0;
    }

As seen above, an equivalent global function works just fine. Why does the above not work?

I'm using clang on OS X 10.10.

Joel
  • 1,295
  • 15
  • 30
  • You're defining the friend implementation inside the class. It needs to be outside, see [this](http://stackoverflow.com/a/7785963/2297365). Here's a [working example](http://coliru.stacked-crooked.com/a/b8fd514c67458c99). – huu May 11 '15 at 23:14
  • @huu: That is unrelated, if you move the type and the functions to a namespace you will notice that ADL won't find them, whether the function is defined inside or outside of the class. – David Rodríguez - dribeas May 11 '15 at 23:16
  • `template std::initializer_list il(std::initializer_listx){return x;} ` is a somewhat dangerous workaround. `bar(il({a,a}))` works. Dangerous because lifetime issues of backing array. – Yakk - Adam Nevraumont May 12 '15 at 01:27

2 Answers2

8

I believe that the problem is that the subexpression1 { a, a } does not really have a type, and as such it does not have associated types or namespaces which in turn means that ADL does not kick in. If you have a function in the global namespace, normal lookup will find it, and it will find that { a, a } can match the function call as the initializer for std::initializer_list<Foo>.

1 The syntax { a, a } is called braced-init-list and it is not really an expression (or subexpression) in the language.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 3
    As a note, there's an exception for `auto` where `std::initializer_list` is deduced. `auto param = {a, a}; bar(param);` works. –  May 11 '15 at 23:20
2

When the compiler sees bar({a,a}), it doesn't know the type of the arguments, so it searches for bar in the global namespace (::), and nowhere else. If you changed that line to Foo{a,a}, then it knows the argument is a Foo, and so also searches the Foo class for functions.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158