Can c++11 parameter packs be used outside templates?

后端 未结 4 1350
慢半拍i
慢半拍i 2020-12-24 06:44

I was wondering if I could have parameter packs consisting of a single, explicitly specified, type. For example, something like this:

#include 

        
相关标签:
4条回答
  • 2020-12-24 06:59

    Why the foo_impl workaround, and not just use initialize_list<int> in foo's signature directly? It clarifies that you accept a variable-size argument list of said type.

    0 讨论(0)
  • 2020-12-24 07:03

    You might specify the type you want to show:

    #include <iostream>
    
    template<typename T>
    void show_type() {}
    
    template<typename T, typename... Rest>
    void show_type(const T& x, Rest... rest)
    {
        std::cout << x << std::endl;
        show_type<T>(rest...);
    }
    
    template<typename... Args>
    void foo(int x, Args... args)
    {
        show_type<int>(x, args...);
    }
    
    struct X { };
    std::ostream& operator<<(std::ostream& o, X)
    {
        o << "x";
        return o;
    }
    
    
    int main()
    {
        foo(1, 2, 3);
        foo(1, 2, 3.0); // Implicit conversion
        // or just
        show_type<int>(1, 2, 3);
        foo(1, 2, X()); // Fails to compile
    }
    
    0 讨论(0)
  • 2020-12-24 07:07

    I realize this is tagged C++11, but the features of C++17/1z works fantastically here so I figured its solution is worth posting:

    template<typename... Ints>
    void foo(Ints... xs)
    {
        static_assert(std::conjunction<std::is_integral<Ints>...>::value);
        (std::cout << xs << '\n', ...);
    }
    
    0 讨论(0)
  • 2020-12-24 07:24
    void foo(int... args) {}
    

    No you cannot write that.

    But you can have the same effect with this approach:

    template<typename ...Ints>
    void foo(Ints... ints) 
    {
       int args[] { ints... }; //unpack ints here
       //use args
    }
    

    With this approach, you can pass all int if you want. If any argument passed to foo is not int or convertible to int, the above code will result in compilation error, as it would be the case with int ...args approach if it were allowed.

    You could also use static_assert to ensure all Ints are indeed int if you want that behaviour:

    template<typename ...Ints>
    void foo(Ints... ints) 
    {
       static_assert(is_all_same<int, Ints...>::value, "Arguments must be int.");
    
       int args[] { ints... }; //unpack ints here
       //use args
    }
    

    Now you've to implement is_all_same meta-function which is not difficult to implement.

    Alright, this is the basic idea. You can write more sophisticated code with variadic templates and with the help of some utility meta-functions and helper functions.

    For lots of work that you can do with variadic arguments, you don't even need to store in args[] array, e.g if you want to print the arguments to std::ostream, then you could just do it as:

    struct sink { template<typename ...T> sink(T && ... ) {} };
    
    template<typename ...Ints>
    void foo(Ints... ints) 
    {
        //some code
    
         sink { (std::cout << ints)... };
    }
    

    Here you create a temporary object of type sink so that you use unpack the template arguments using list-initialization syntax.

    Last you could just use std::initializer_list<int> itself:

    void foo(initializer_list<int> const & ints) 
    {
    
    }
    

    Or std::vector<int> in case if you need vector-like behavior inside foo(). If you use any of these, you have to use {} when calling the function as:

    f({1,2,3});
    

    That may not be ideal but I think with the advent of C++11 you will see such code very frequently!

    0 讨论(0)
提交回复
热议问题