Inserting a variadic argument list into a vector?

后端 未结 6 1056
别那么骄傲
别那么骄傲 2020-12-09 02:44

Forgive me if this has been answered already, as I couldn\'t find it...

Basically I have an object that needs to take a variadic argument list in it\'s constructor

相关标签:
6条回答
  • 2020-12-09 03:04

    You can't use a variadic argument list unless it's a template, you can, as stated, use a initializer_list like this:

    class GenericNode {
    public:
        GenericNode(std::initializer_list<GenericNode*> inputs) : inputs_(inputs)
        {
        }
    private:
        std::vector<GenericNode*> inputs_;
    };
    
    template <class ... T>
    GenericNode* foo(T ... t)
    {
        return new GenericNode({t...});
    }
    
    0 讨论(0)
  • 2020-12-09 03:12

    Another way to do it:

    #include <iostream>
    #include <vector>
    
    using std::vector;
    
    template <typename T>
    void variadic_vector_emplace(vector<T>&) {}
    
    template <typename T, typename First, typename... Args>
    void variadic_vector_emplace(vector<T>& v, First&& first, Args&&... args)
    {
        v.emplace_back(std::forward<First>(first));
        variadic_vector_emplace(v, std::forward<Args>(args)...);
    }
    
    struct my_struct
    {
        template <typename... Args>
        my_struct(Args&&... args)
        {
            variadic_vector_emplace(_data, std::forward<Args>(args)...);
        }
    
        vector<int>& data() { return _data; }
    
    private:
      vector<int> _data;
    };
    
    
    int main()
    {
        my_struct my(5, 6, 7, 8);
    
        for(int i : my.data())
          std::cout << i << std::endl;
    }
    
    0 讨论(0)
  • 2020-12-09 03:15
        // inputs_.push_back(inputs)...;
    

    This doesn't work because you can't expand a parameter pack as a statement, only in certain contexts such as a function argument list or initializer-list.

    Also your constructor signature is wrong, if you're trying to write a variadic template it needs to be a template!

    Once you write your constructor signature correctly the answer is easy, just construct the vector with the pack expansion:

    #include <vector>
    
    class GenericNode
    {
    public:
      template<typename... T>
        GenericNode(T*... inputs) : inputs_{ inputs... }
        { }
    private:
        std::vector<GenericNode*> inputs_;
    };
    

    (You could instead have set it in the constructor body with:

    inputs_ = { inputs... };
    

    but the cool kids use member initializers not assignment in the constructor body.)

    The downside of this solution is that the template constructor accepts any type of pointer arguments, but will then give an error when trying to construct the vector if the arguments aren't convertible to GenericNode*. You could constrain the template to only accept GenericNode pointers, but that's what happens automatically if you do what the other answers suggest and make the constructor take a std::initializer_list<GenericNode*>, and then you don't need any ugly enable_if SFINAE tricks.

    0 讨论(0)
  • 2020-12-09 03:20

    I recently wrote the following function that takes a string with {1} , {2} , {3} ... in it and substitutes the argument list. I ran in to the same problem until I decided to let the compiler work it out for itself with the auto keyword.

    #include <string>
    #include <vector>
    
    using std::string;
    using std::vector;
    
    template<typename S, typename... Args>
    string interpolate( const S& orig , const Args&... args)
    {
       string out(orig);
    
       auto va = {args...};
       vector<string> v{va};
    
       size_t i = 1;
    
       for( string s: v)
        {
          string is = std::to_string(i);
          string t = "{" +  is + "}";
          try
             {
               auto pos = out.find(t);
               if(pos != out.npos) 
                  {
                     out.erase(pos, t.length());
                     out.insert( pos, s); 
                  }                  
               i++;
             }
        catch( std::exception& e)
           {
              std::cerr << e.what() << std::endl;
           }
         } // for
    
       return out;
     }
    

    Apparently that is good enough as long as the types line up correctly. In this case I am using only std::string throughout. I think this is an elegant technique, but it may have drawbacks.

    0 讨论(0)
  • 2020-12-09 03:28

    The best thing would be to use an initializer list

    #include <initializer_list>
    #include <vector>
    class GenericNode {
    public:
        GenericNode(std::initializer_list<GenericNode*> inputs) 
            :inputs_(inputs) {} //well that's easy
    private:
        std::vector<GenericNode*> inputs_;
    };
    int main() {
        GenericNode* ptr;
        GenericNode node{ptr, ptr, ptr, ptr};
    } //compilation at http://stacked-crooked.com/view?id=88ebac6a4490915fc4bc608765ba2b6c
    

    The closest to what you already have, using C++11 is to use the vector's initializer_list:

        template<class ...Ts>
        GenericNode(Ts... inputs) 
            :inputs_{inputs...} {} //well that's easy too
        //compilation at http://stacked-crooked.com/view?id=2f7514b33401c51d33677bbff358f8ae
    

    And here's a C++11 version with no initializer_lists at all. It's ugly, and complicated, and requires features missing from many compilers. Use the initializer list

    template<class T>
    using Alias = T;
    
    class GenericNode {
    public:
        template<class ...Ts>
        GenericNode(Ts... inputs) { //SFINAE might be appropriate
             using ptr = GenericNode*;
             Alias<char[]>{( //first part of magic unpacker
                 inputs_.push_back(ptr(inputs))
                 ,'0')...,'0'}; //second part of magic unpacker
        }
    private:
        std::vector<GenericNode*> inputs_;
    };
    int main() {
        GenericNode* ptr;
        GenericNode node(ptr, ptr, ptr, ptr);
    } //compilation at http://stacked-crooked.com/view?id=57c533692166fb222adf5f837891e1f9
    //thanks to R. Martinho Fernandes for helping me get it to compile
    

    Unrelated to everything, I don't know if those are owning pointers or not. If they are, use std::unique_ptr instead.

    0 讨论(0)
  • 2020-12-09 03:28
    class Blob
     {
        std::vector<std::string> _v;
     public:
    
        template<typename... Args>
        Blob(Args&&... args)
        : _v(std::forward<Args>(args)...)
        {  }
    
    };
    
    int main(void)
    {
        const char * shapes[3] = { "Circle", "Triangle", "Square" };
    
        Blob b1(5, "C++ Truths"); 
        Blob b2(shapes, shapes+3);
    }
    

    Example from C++11 Truths looks simple enough...;) Not a complete solution but might give you some ideas.

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