Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Supply initializer-list constructor for class template

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.

like image 327
cfa45ca55111016ee9269f0a52e771 Avatar asked Oct 22 '22 17:10

cfa45ca55111016ee9269f0a52e771


1 Answers

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.

like image 98
ecatmur Avatar answered Nov 12 '22 23:11

ecatmur