29

如果这已经回答了,请原谅我,因为我找不到它......

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

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

6 回答 6

29

最好的办法是使用初始化列表

#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 是使用向量的 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改用。

于 2012-12-20T20:11:33.997 回答
9
    // 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指针,但是如果您按照其他答案的建议并使构造函数采用 a std::initializer_list<GenericNode*>,那么这就是自动发生的事情,然后您就不需要任何丑陋的enable_ifSFINAE 技巧。

于 2012-12-20T20:55:14.753 回答
3

除非它是模板,否则您不能使用可变参数列表,如前所述,您可以像这样使用 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...});
}
于 2012-12-20T20:06:42.537 回答
2
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 Truths 中的示例看起来很简单......;) 不是一个完整的解决方案,但可能会给你一些想法。

于 2013-10-29T02:11:03.210 回答
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;
}
于 2012-12-21T00:18:54.950 回答
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 。我认为这是一种优雅的技术,但它可能有缺点。

于 2018-07-28T04:47:15.570 回答