11

c++11 (gcc 4.7.2 など) の現在の状態では、可変引数を取ることができるコンストラクターが必要な場合、可変長テンプレートを使用するか、または aを使用するかをどのように選択すればよいですか?std::initializer_list

4

3 に答える 3

15

可変個引数テンプレートを使用すると、さまざまなタイプの引数を提供できますが、std::initializer_listは引数のタイプでテンプレート化されます。これは、リスト内のすべての要素のタイプが同じである必要があることを意味します(または、基になるタイプに変換可能ですが、絞り込み変換は許可されません)。これが望ましいかどうかに応じて、どちらかを選択できます。

また、完全な転送が必要な場合は、通常、可変個引数テンプレートがデフォルトの選択肢です。構文形式T&&は左辺値参照と右辺値参照の両方にバインドできますが、次の場合は同様の型の推定を実行できませんinitializer_list

struct A
{
    // Deduces T& for lvalue references, T for rvalue references, and binds to both
    template<typename... Ts>
    A(Ts&&...) { }

    // This is an rvalue reference to an initializer_list. The above type deduction
    // does not apply here
    template<typename T>
    A(initializer_list<T>&&) { }
};

initializer_listまた、別の実行可能なコンストラクターが存在する場合でも、統一された初期化構文(つまり中括弧)を使用すると、デフォルトでを受け入れるコンストラクターが呼び出されることに注意してください。これはあなたが望むものかもしれないし、そうでないかもしれません:

struct A
{
    A(int i) { }
};

struct B
{
    B(int) { }
    B(std::initializer_list<A>) { }
};

int main()
{
    B b {1}; // Will invoke the constructor accepting initializer_list
}
于 2013-02-16T19:29:27.153 に答える
4

可変個引数テンプレートを使用すると、コンパイル中に引数の数がわかります (および 経由でアクセスできますsizeof...)。を使用するstd::initializer_listと、引数の数は実行時にのみわかります。したがって、決定の一部は、必要な場合、または引数の数を知りたい場合によって異なります。

于 2013-02-16T20:39:12.457 に答える
3

常に可変個引数テンプレートを選択し、std::initializer_list可能な限り避けることをお勧めします。

これは私がstd::vectorC++11で実装した方法です:

#include <iostream>
#include <vector>

struct exp_sequence {
  template <typename... T>
  exp_sequence(T&&...) {}
};

struct from_arglist_t {} from_arglist;

template <typename T>
class my_vector {
  std::vector<T> data;

public:
  my_vector(int n, T const& e) : data(n, e) {}

  template <typename... Args>
  my_vector(from_arglist_t, Args&&... args) {
    data.reserve(sizeof...(Args));
    exp_sequence{(data.push_back(std::forward<Args>(args)),1)...};
  }

  std::size_t size() { return data.size(); }
};

int main()
{
  std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2
  std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13

  my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13
  my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13
  my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2
  my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2
}

その理由は、に示されているように、ジェネリックコードmainで使用initializer_listすると、選択した括弧のタイプに応じて異なる動作が発生する可能性があります。このようなコンストラクターを追加することで、コードを黙って変更する可能性もあります。

もう1つの理由は、移動専用タイプです。

//std::vector<move_only> m1{move_only{}}; // won't compile
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine
于 2013-02-16T19:38:11.800 に答える