5

I have a class template Templ with template parameter T, and the Templ class has a data member of type T, called obj. I wrote a variadic constructor template which forwards the arguments to obj's constructor:

template <class T>
class Templ
{
public:
     template <class... Args> explicit Templ (Args&&... args)
     : obj (std::forward<Args>(args)...)
     {
     }
private:
     T obj;
};

Now I realized that type T may be a class with an init-list constructor, and I want it to be accessible via Templ. So I checked what std::list::emplace and std::make_shared do. They have a variadic function like mine, but they don't have overrides taking an init-list. For some reason.

So first question: why? I mean, what if I use some class T with an init-list ctor, and then I use std::list<T>? Why does list::emplace not have a version that takes an initializer_list? Maybe there's a good reason I should do it either... so I want to know.

Also, regardless of what the STL does - should I supply an init-list ctor as good design? I mean, it's just like the variadic ctor, right? Allowing the user to choose any type or class T to use with Templ<> and directly call any ctor defined for T. Even if it's a ctor taking an init-list.

1 Answers1

4

The problem with forwarding initializer_list constructors is that all but the most trivial argument types aren't deducible (Templates don't always guess initializer list types):

#include <map>
template<typename T> struct U {
   T t;
   template<typename...A> explicit U(A&&...a): t(std::forward<A>(a)...) {}
   template<typename L, typename = typename std::enable_if<
      std::is_constructible<T, std::initializer_list<L>>::value>::type>
      explicit U(std::initializer_list<L> l): t(l) {}
};
U<std::map<int, int>> m{{{0, 1}, {2, 3}}};  // fails, couldn't deduce 'L'

Since you'd have to write m{std::initializer_list<...>{...}} in most cases, there's not much point providing it just for primitives, and certainly not for the standard to do so.

If you think that any interesting initializer_list arguments are likely to be for container types, you could look at the approach taken in Optionally supporting initializer_list construction for templates maybe wrapping containers.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I don't expect to wrap containers, but I do want to support a class which has an init-list ctor. In which cases does the deduction not work? If the class used as the template parameter has a ctor taking an init-list of primitive-type variables, or an init-list of objects of a class I defined (not a pair or a container or anything like that), will it work? Also, do I have to use enable_if? What happens if I don't? – cfa45ca55111016ee9269f0a52e771 Feb 11 '13 at 13:28
  • 1
    @fr33domlover if your initializer is of the form `{x1, x2, ...}` where all `x` are prvalue expressions of the same type then you're fine; the problem is if you have nested braces. The `enable_if` is necessary if you want to use uniform initialization; `initializer_list` constructors are greedy (13.3.1.7). – ecatmur Feb 11 '13 at 13:41
  • 1
    We certainly need an initializer_tuple intrinsic type for the next c++ that allows perfect forwarding of braced initializer lists, be they homogenous or heterogenous. – Johannes Schaub - litb Feb 11 '13 at 19:17