7

I want to write a function that can be used with an argument that otherwise could directly occur in a range-based loop:

template <typename Iterable>
void sayIt(const Iterable& stuff) {
    for (const auto& e : stuff) {
        cout << e << endl;
    }
}

This works fine with stl containers and other types, but not with a brace-enclosed initializer:

std::vector<std::string> baz(2, "sorry");
sayIt(baz);              // okay
sayIt({"foo", "bar"});   // not okay

Is there a way to make the function work with both?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
shinjin
  • 2,858
  • 5
  • 29
  • 44

1 Answers1

9

Braced-init-list has no type and cause template argument deduction failing.

Non-deduced contexts

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

  1. The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:

You can specify the template argument as std::initializer_list explicitly to bypass the deduction,

sayIt<std::initializer_list<std::string>>({"foo", "bar"});

Or add another overload taking std::initializer_list.

template <typename T>
void sayIt(std::initializer_list<T> stuff) {
    sayIt<decltype(stuff)>(stuff);
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    If I was to write `auto init = { "foo", "bar" };` and then `sayIt(init);` it will work. The compiler is deducing `init` as type `std::initializer_list`. But doesn't it doesn't do the same with `{ "foo", "bar" }`. Why? – jignatius Aug 14 '20 at 10:41
  • 1
    @jignatius Because there's special rule for [auto type deduction](https://en.cppreference.com/w/cpp/language/template_argument_deduction#Other_contexts). With copy-list-initalization, `init` will be deduced as `std::initializer_list`. – songyuanyao Aug 14 '20 at 10:49