C++11 variable number of arguments, same specific type

前端 未结 6 1744
难免孤独
难免孤独 2020-12-05 01:55

Question is simple, how would I implement a function taking a variable number of arguments (alike the variadic template), however where all arguments have the same type, say

相关标签:
6条回答
  • 2020-12-05 02:14

    If you don't want to use brace-based initializer_list/vector and want to keep the arguments separate in form of argument pack, then below solution checks it at compile time using recursive static_asserts:

    #include<type_traits>
    
    template<typename T1, typename T2, typename... Error>
    struct is_same : std::false_type {};
    
    template<typename T, typename... Checking>
    struct is_same<T, T, Checking...> : is_same<T, Checking...> {}; 
    
    template<typename T>
    struct is_same<T,T> : std::true_type {};
    
    template<typename... LeftMost>
    void func (LeftMost&&... args)
    {
      static_assert(is_same<typename std::decay<LeftMost>::type...>::value, 
                    "All types are not same as 'LeftMost'");
      // ...
    }
    
    int main ()
    {
      int var = 2;
      func(1,var,3,4,5);  // ok
      func(1,2,3,4.0,5); // error due to `static_assert` failure
    }
    

    Actually this solution would check all the arguments with respect to the first argument. Suppose it was double then everything would be checked against double.

    0 讨论(0)
  • 2020-12-05 02:18

    A possible solution is to make the parameter type a container that can be initialized by a brace initializer list, such as std::initializer_list<int> or std::vector<int>. For example:

    #include <iostream>
    #include <initializer_list>
    
    void func(std::initializer_list<int> a_args)
    {
        for (auto i: a_args) std::cout << i << '\n';
    }
    
    int main()
    {
        func({4, 7});
        func({4, 7, 12, 14});
    }
    
    0 讨论(0)
  • 2020-12-05 02:23

    Here's a version that removes the function from the overload set, instead of giving a static_assert. This is allows you to provide other overloads of the function that could be used when the types aren't all the same, rather than a fatal static_assert that can't be avoided.

    #include <type_traits>
    
    template<typename... T>
      struct all_same : std::false_type { };
    
    template<>
      struct all_same<> : std::true_type { };
    
    template<typename T>
      struct all_same<T> : std::true_type { };
    
    template<typename T, typename... Ts>
      struct all_same<T, T, Ts...> : all_same<T, Ts...> { };
    
    template<typename... T>
    typename std::enable_if<all_same<T...>::value, void>::type
    func(T...)
    { }
    

    If you want to support perfect forwarding you probably want to decay the types before checking them, so that the function will accept a mix of lvalue and rvalue arguments as long as they have the same type:

    template<typename... T>
    typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
    func(T&&...)
    { }
    

    Alternatively, if you have a general purpose trait for testing the logical conjunction you can do it using std::is_same instead of writing your own all_same:

    template<typename T, typename... Ts>
    typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
    func(T&&, Ts&&...)
    { }
    

    Because this requires at least one argument you'd also need another overload to support the zero-argument case:

    void func() { }
    

    The and_ helper can be defined like so:

    template<typename...>
      struct and_;
    
    template<>
      struct and_<>
      : public std::true_type
      { };
    
    template<typename B1>
      struct and_<B1>
      : public B1
      { };
    
    template<typename B1, typename B2>
      struct and_<B1, B2>
      : public std::conditional<B1::value, B2, B1>::type
      { };
    
    template<typename B1, typename B2, typename B3, typename... Bn>
      struct and_<B1, B2, B3, Bn...>
      : public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
      { };
    
    0 讨论(0)
  • 2020-12-05 02:23

    I think you can do this by specifying a concrete type when chewing your arguments out of the argument pack. Something like:

    class MyClass{};
    class MyOtherClass{};
    
    void func()
    {
        // do something
    }
    
    template< typename... Arguments >
    void func( MyClass arg, Arguments ... args )
    {
        // do something with arg
        func( args... );
        // do something more with arg
    }
    
    
    void main()
    {
        MyClass a, b, c;
        MyOtherClass d;
        int i;
        float f;
    
        func( a, b, c );    // compiles fine
        func( i, f, d );    // cannot convert
    }
    

    In the generic case void func( MyClass arg, Arguments ... args ) would become void func( arg, Arguments ... args ) with a template type T.

    0 讨论(0)
  • 2020-12-05 02:23

    Because I don't think I saw this solution, you could write a specific function for every type (in your case, just int) then a forwarding function taking variadic argument types.

    Write each specific case:

    then for each specific case:

    // only int in your case
    void func(int i){
        std::cout << "int i = " << i << std::endl;
    }
    

    Then your forwarding function like this:

    template<typename Arg0, typename Arg1 typename ... Args>
    void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
        func(std::forward<Arg0>(arg0));
        func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
    }
    

    This is good because it is expandable for when you want to accept maybe another type too.

    Used like this:

    int main(){
        func(1, 2, 3, 4); // works fine
        func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
    }
    
    0 讨论(0)
  • 2020-12-05 02:38

    @Skeen How about this?

    template <typename T>
    void func_1(std::initializer_list<T>&& a) {
        // do something
    } 
    
    template <typename... T>
    void func(T&&... a) {
        func_1({std::forward<T>(a)...});
    } 
    
    int main() {
        func(1, 2, 3);
        // func(1, 2, 3, 4.0); // OK doesn't compile
    }
    
    0 讨论(0)
提交回复
热议问题