3

I am trying to construct a mixin template whose bases are all passed as variadic template parameters. I would like to construct the mixin by passing the constructor parameters of each mixin class as a parameter to a variadic template constructor

The variadic template constructor when called with objects of each mixin class type compiles. But if I pass the constructor parameter of each mixin class (all the classes have a single parameter constructor), it doesn't compile

I am using gcc 7.0 with -std=c++1z

What am I doing wrong?

 #include <vector>
 #include <string>
 #include <unordered_map>
 #include <iostream>

 template < typename... T >
 struct Mixin : T...
 {
     Mixin() = delete;
     Mixin(Mixin const &) = delete;
     Mixin(Mixin &&) = delete;

     template < typename... U >
     Mixin(U &&... v) : T(std::forward < U >(v))...
     {
     }
 };

 int main()
 {
     using A = std::vector < std::string >;
     using B = std::unordered_map < std::string, std::string >;
     using C = std::string;
     using M = Mixin < A, B, C >;

     //  This doesn't compile
     M m1{{"hello", "world"}, { {"hello",  "world" }, {"world",  "hello"} }, "hello"};

     //  This compiles
     A a({"hello", "world"}); B b({ {"hello",  "world" }, {"world",  "hello"} }); C c("hello");
     M m2{a, b, c};
 }
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
zrb
  • 851
  • 7
  • 16

1 Answers1

2

The problem here is that std::initializer_list cannot be deduced from a forwarding-reference. In fact, explicitly specifying std::initializer_list makes your code compile:

M m1{
    std::initializer_list<std::string>{"hello", "world"}, 
    std::initializer_list<std::pair<const std::string, std::string>>{{"hello",  "world" },{"world",  "hello"} }, 
    "hello"};

wandbox example

You can find more information about std::initializer_list and deduction in this question.


You can force deduction of std::initializer_list by creating an helper make_il function:

template <typename... Ts>
auto make_il(Ts&&... xs) 
{ 
    return std::initializer_list<std::common_type_t<Ts...>>{
        std::forward<Ts>(xs)...}; 
}

Your final code will look like:

using namespace std::literals::string_literals;          
using kvp = std::pair<const std::string, std::string>;

M m1{
    make_il("hello"s, "world"s), 
    make_il(kvp("hello"s,  "world"s), kvp("world"s,  "hello"s)), 
    "hello"};

wandbox example

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416