Search code examples
c++c++11templatesparameter-pack

Can we use int... as an argument for template parameter pack directly?


Here is my problem:

template <class... Ts>
struct iniClass
{
private:
    std::initializer_list<int> m_data;
    int x;

public:
    iniClass(Ts... vars)
        : m_data({vars...}), x(8)
    {
        std::cout << "created object with first constructor\n";
        prin();
    }
    void prin()
    {
        for (auto var : m_data)
        {
            std::cout << var << ", ";
        }
        std::cout << '\n';
    }
};

int main()
{
    iniClass<std::initializer_list<int>> il1({1, 2, 3, 4}); // Work like a charm!
    iniClass<int...> il2(1, 2, 3, 4);                       // Didn't work, How to make this work?
}

Can we use int... (,and generally primativeTYPE...) as an argument for template parameter pack directly?
Is there a way to make it work in that way?


Solution

  • No, this feature is currently not part of the C++ standard. The closest thing we have is:

    // P1219 proposed syntax
    void foo(int... args);
    
    // C++20
    void foo(std::convertible_to<int> auto&&... args);
    
    // C++17
    template <typename... Ts>
    auto foo(Ts&&... args)
      -> std::enable_if_t<(std::is_convertible_v<Ts, int> && ...)>;
    
    // C++14
    template <typename... Ts>
    auto foo(Ts&&... args)
      -> std::enable_if_t<CONJUNCTION<std::is_convertible<Ts, int>...>::value>;
    // TODO: implement a conjunction trait like std::conjunction
    
    // C++11
    template <typename... Ts>
    auto foo(Ts&&... args)
      -> typename std::enable_if<CONJUNCTION<std::is_convertible<Ts, int>...>::value>::type;
    

    There is a well-received proposal which adds this feature though. See P1219 - Homogeneous variadic function parameters. This proposal has been rejected for C++23 by EWG (Evolution Working Group), however, it is possible that a revised version could make it into a future standard.

    It doesn't really look like you need this feature though. You could just use std::array, which does support construction like:

    // std::make_array, (from library fundamentals TS v2) (C++26?)
    // you could implement this yourself in C++11
    auto arr = std::experimental::make_array<int>(1, 2, 3, 4, 5);
    
    // std::to_array, since C++20
    // you could implement this yourself in C++11
    auto arr = std::to_array<int>({1, 2, 3, 4, 5});
    
    // CTAD, since C++17
    std::array arr{1, 2, 3, 4, 5};
    
    // C++11, works out of the box
    std::array<int, 5> arr{1, 2, 3, 4, 5};
    
    // C-style arrays
    int arr[] {1, 2, 3, 4, 5};
    

    Your iniClass is basically an array anyway. Also, it's a bad idea to ever store std::initializer_list, because it doesn't own its contents. It's like a pointer or reference.