web-dev-qa-db-ja.com

可変個引数リストをベクトルに挿入しますか?

私はそれを見つけることができなかったので、これがすでに答えられているならば、私を許してください...

基本的に、コンストラクターで可変引数リストを取得し、引数をベクトルに格納する必要があるオブジェクトがあります。可変個引数コンストラクターの引数からベクトルを初期化するにはどうすればよいですか?

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

best事は、初期化リストを使用することです

#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を使用してください。

26
Mooing Duck
    // 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*>を取得させると、それが自動的に行われます。醜いものは必要ありません。 enable_ifSFINAEのトリック。

6
Jonathan Wakely

テンプレートでない限り、可変個引数リストを使用することはできません。前述のように、次のような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...});
}
2
Luke B.

それを行う別の方法:

#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;
}
1
Gigi
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は十分に単純に見えます...;)完全な解決策ではありませんが、いくつかのアイデアが得られる可能性があります。

1
Mattias

私は最近、{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のみを使用しています。これはエレガントなテクニックだと思いますが、欠点があるかもしれません。

0
Chris Reid