41

I'm trying to use std::make_unique to instanciate a class whose constructor is to receive an std::initializer_list. Here a minimal case :

#include <string>
#include <vector>
#include <initializer_list>
#include <memory>

struct Foo {
    Foo(std::initializer_list<std::string> strings) : strings(strings) {}

    std::vector<std::string> strings;
};

int main(int, char**) {

    auto ptr = std::make_unique<Foo>({"Hello", "World"});

    return 0;
}

You can see on Coliru that it doesn't build :

main.cpp:14:56: error: no matching function for call to 'make_unique(<brace-enclosed initializer list>)'
     auto ptr = std::make_unique<Foo>({"Hello", "World"});

So, is make_unique reportedly unable to use initializer_lists ? Is there a bug in GCC 4.9.1 ? Or did I overlook something ?

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 4
    Braced lists cannot be deduced by template argument deduction. Try `make_unique(std::initializer_list({"Hello", "World"}))`. – Kerrek SB Oct 15 '14 at 09:49
  • @KerrekSB Well, that looks like an answer to me :) – Quentin Oct 15 '14 at 09:51
  • 1
    @KerrekSB Yes it does ! But ugh, that syntax. `initializer_list`s are definitely strange gizmos. In that particular case I think I'll just construct the `unique_ptr` from a `new` call. – Quentin Oct 15 '14 at 09:55
  • 3
    That's a shame, but understandable. `std::initializer_list` is a terrible misdesign. Sorry about that. – Kerrek SB Oct 15 '14 at 09:56
  • Hey, try this: `std::make_unique>({"Hello", "World"})` – Kerrek SB Oct 15 '14 at 09:58
  • @Quentin how about: `auto il { "Hello"s, "World"s }; auto ptr = make_unique(il);` ? – Piotr Skotnicki Oct 15 '14 at 09:58
  • @PiotrS.: Good call, mind if I steal that? – Kerrek SB Oct 15 '14 at 10:01
  • @KerrekSB that's fine. but I can't recall the proper rule that will be introduced, whether `auto` will require `=` to deduce `initializer_list` or not – Piotr Skotnicki Oct 15 '14 at 10:03
  • @KerrekSB and it requires `using std::operator "" s;`, as it seems that OP qualifies all names with `std::` – Piotr Skotnicki Oct 15 '14 at 10:08
  • @PiotrS.: I'm not sure if this has changed, I thought this has always been well-defined in C++11? Maybe not, I don't have a non-head standard ready. – Kerrek SB Oct 15 '14 at 10:09
  • @PiotrS.: Thanks - I was just wondering why I didn't have that literal. Shame, why isn't that in the global namespace. – Kerrek SB Oct 15 '14 at 10:09
  • I'm looking at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3588.txt - Q1. Not sure if related since I'm not really getting that part.. – Marco A. Oct 15 '14 at 10:12
  • @KerrekSB [N3922](http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2014/n3922.html) proposes to add a change that will require `=` to use `initializer_list` in `auto` – Piotr Skotnicki Oct 15 '14 at 10:14
  • @MarcoA.: Deduction from `auto` is in [dcl.spec.auto] . – Kerrek SB Oct 15 '14 at 10:14
  • @PiotrS.: Interesting. I don't think it has been voted in yet, definitely not into C++14. It may well make it into C++17, though. – Kerrek SB Oct 15 '14 at 10:18

2 Answers2

61

std::make_unique is a function template which deduces the argument types which are passed to the object constructor. Unfortunately, braced lists are not deducible (with an exception for auto declarations), and so you cannot instantiate the function template when that missing parameter type.

You can either not use std::make_unique, but please don't go that route – you should avoid naked news as much as you can, for the children's sake. Or you can make the type deduction work by specifying the type:

  • std::make_unique<Foo>(std::initializer_list<std::string>({"Hello", "World"}))

  • std::make_unique<Foo, std::initializer_list<std::string>>({"Hello", "World"})

  • auto il = { "Hello"s, "World"s }; auto ptr = std::make_unique<Foo>(il);

The last option uses the special rule for auto declarations, which (as I hinted above) do in fact deduce an std::initializer_list.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
10

If you're prepared to type a few extra characters, you can do this:

auto ptr = std::make_unique<Foo>( make_init_list( { "Hello"s , "World"s } ));

where init_list is defined as

template<typename T>
std:: initializer_list<T> make_init_list ( std:: initializer_list<T> && l ) {
    return l;
}

This allows the deduction to occur, and is convenient if there are many places in the code you must do this.

(Works on clang 3.9 and gcc 6.2.0. I got it to work on g++-4.8.4 also, except I had to tweak the std::string-literal and change to make_shared. But the deduction of T within make_init_list worked fine.)

Actually, it this an extension? Is successful deduction for make_init_list required by the standard or not?

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88