Search code examples
c++c++17variadic-templatestemplate-argument-deduction

constructor with variadic template arguments of the same type does not compile


I am trying to construct a class having using variadic template constructor. The template arguments are all the same type.

I am using Visual C++ compiler with C++ 17, but code does not build; this code is actually crashing the compiler.

Is that possible in C++17 ?

 struct Alfa {
     template<int... T>
     Alfa(const T&... all_args) {
         std::tuple<T...> data(all_args...);       
     }    
 };

 Alfa alfa(1,2,3,4);

Solution

  • The template arguments are all the same type. [...] is that possible in C++17 ?

    Yes, it's possible.

    But non in a so simple way and with some drawbacks.

    You can write a constructor accepting arguments of different type; this is simple

     template <typename ... Ts>
     Alfa (const Ts & ... as) 
      { std::tuple<Ts...> data{as...}; } 
    

    but this permit that the Ts... types are different.

    You can impose that all types are the same, using SFINAE, as follows

     template <typename T, typename ... Ts,
               std::enable_if_t<(std::is_same_v<T, Ts> && ...), bool> = true>
     Alfa (T const & a0, Ts const & ... as)
      {
         std::tuple<T, Ts...> data0{a0, as...};       
         std::array           data1{a0, as...};
      }  
    

    so your constructor is enabled only if all Ts... types, following the first T, are exactly the same as T

    Drawback: works with

       Alfa alfa{1, 2, 3, 4};
    

    but gives an error with

       Alfa alfa{1l, 2l, 3, 4l};  <-- 3 isn't long
    

    because 3 is convertible to long (1l is long) but isn't long.

    So you can check if the following Ts... are convertible to T, instead if they are equals

     template <typename T, typename ... Ts,
               std::enable_if_t<(std::is_convertible_v<Ts, T> && ...), bool> = true>
     Alfa (T const & a0, Ts const & ... as)
      {
         std::tuple<T, Ts...>             data0{a0, as...};       
         std::array<T, sizeof...(Ts)+1u>  data1{a0, as...};
      }   
    

    but this way you give to T a bigger importance to other types (works if all Ts... are convertible to T but not if T is convertible to one of the Ts...) so I suppose that the better solution is check if there is a common type

     template <typename ... Ts,
               typename CT = std::common_type_t<Ts...>>
     Alfa (Ts const & ... as)
      {
         std::tuple<Ts...>              data0{as...};       
         std::array<CT, sizeof...(Ts)>  data1{as...};
      }