将可变参数列表插入向量?

时间:2012-12-20 19:13:44

标签: c++ c++11 variadic-functions

请原谅我已经回答过,因为我找不到......

基本上我有一个对象需要在它的构造函数中使用可变参数列表并将参数存储在向量中。如何从可变参数构造函数的参数初始化向量?

class GenericNode {
public:
    GenericNode(GenericNode*... inputs) {
            /* Something like... */
        // inputs_.push_back(inputs)...;
}
private:
    std::vector<GenericNode*> inputs_;
};

6 个答案:

答案 0 :(得分:25)

最好的事情是使用初始化列表

#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

使用C ++ 11最接近你已经拥有的是使用vector的initializer_list:

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

这是一个没有initializer_lists的C ++ 11版本。它很丑陋,很复杂,需要许多编译器缺少功能。使用初始化列表

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

与一切无关,我不知道那些是否拥有指针。如果是,请改为使用std::unique_ptr

答案 1 :(得分:6)

    // inputs_.push_back(inputs)...;

这不起作用,因为您不能将参数包扩展为语句,仅在某些上下文中,例如函数参数列表或初始化列表。

此外,您的构造函数签名是错误的,如果您正在尝试编写可变参数模板,则需要将其作为模板!

正确编写构造函数签名后,答案很简单,只需使用包扩展构造向量:

#include <vector>

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

(您可以使用以下命令在构造函数体中设置它:

inputs_ = { inputs... };

但酷孩子使用成员初始化器而不是在构造函数体中赋值。)

此解决方案的缺点是模板构造函数接受任何类型的指针参数,但如果参数不可转换为GenericNode*,则在尝试构造向量时会出错。您可以将模板限制为仅接受GenericNode指针,但如果您执行其他答案建议的操作并使构造函数采用std::initializer_list<GenericNode*>,则会自动执行此操作,然后您不需要任何丑陋{ {1}} SFINAE技巧。

答案 2 :(得分:2)

你不能使用可变参数列表,除非它是一个模板,你可以按照说明使用这样的initializer_list:

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...});
}

答案 3 :(得分:1)

另一种方法:

#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;
}

答案 4 :(得分:1)

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);
}

来自C ++ 11的示例真相看起来很简单......;) 不是一个完整的解决方案,但可能会给你一些想法。

答案 5 :(得分:0)

我最近编写了以下函数,该函数需要一个带有 {1},{2},{3} ...并替换参数列表。我遇到了同样的问题,直到我决定让编译器使用auto关键字自行解决。

#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;
 }

显然,只要类型正确排列就足够了。 在这种情况下,我始终只使用std :: string。 我认为这是一种优雅的技术,但是可能会有缺点。