5

私は大きなシーケンスの2つのベクトルを持つクラスを設計しようとしています。どのようにl-value ref、r-value ref、およびinitializer_listのすべてを許可するテンプレートコンストラクタを作成できますか?

std::vector<double> factory() { 
    return std::vector<double>{1,2,3}; // it actually generates a large sequence of double 
} 

struct my_class { 
    my_class(const std::vector<double>& x, const std::vector<double>& y) 
    : m_x(x), m_y(y) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

int main() { 
    my_class c(factory(), factory()); 
    my_class c2(factory(), {0.5, 1, 1.5}); 
} 

よく動作しますが、ベクターの移動コンストラクタは使用しません。だから私は完璧な転送でr値の参照を受け入れるようにコンストラクタを変更しようとしました。

struct my_class { 
    template<typename X, typename Y> 
    my_class(X&& x, Y&& y 
      , typename std::enable_if<std::is_convertible<X, std::vector<double> >::value && 
             std::is_convertible<Y, std::vector<double> >::value>::type * = 0 
      ) 
    : m_x(std::forward<X>(x)), m_y(std::forward<Y>(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

これで問題が発生しました。 initializer_listを使ってインスタンスを構築しようとすると、このようなエラーが出ます。

$ g++ -W -Wall -std=gnu++0x a.cpp 
a.cpp: In function ‘int main()’: 
a.cpp:34:32: error: no matching function for call to ‘my_class::my_class(std::vector<double>, <brace-enclosed initializer list>)’ 
a.cpp:17:18: note: candidate is: my_class::my_class(const my_class&) 

私はstd::initializer_list<double>std::vector<double>に変換可能でないかもしれないと思ったが、それは実際に転換され、私はenable_if引数なしにしようとしたとき、私は同じエラーを得ました。何か不足していますか?

+0

g ++のどのバージョンを使用していますか? IIRCの 'initializer_list'サポートはいくつかの最近のバージョンでは不完全でした。 – Flexo

+0

@awoodland gcc 4.6.2を使用しています。 「不完全」とはどういう意味ですか? – kukyakya

答えて

7

優先イディオムは、手動で、その後pass by valueにあり、メンバー初期化子リスト内を移動:

struct my_class { 
    my_class(std::vector<double> x, std::vector<double> y) 
    : m_x(std::move(x)), m_y(std::move(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

これは、すべての可能な引数と協力し、適度に高速になります。

  • あなたはベクトルを渡した場合左辺の場合、ベクトルはにコピーされ、xにコピーされ、次にm_xに移動されます。
  • あなたはベクトル右辺値を渡すと、ベクターはxを移動してからm_xに再び移動されます。
  • 初期化子リストを渡すと、xはそのリストから初期化され、m_xに移動します。

代替が完璧転送ですが、それは彼がに渡すことが何を知っているクライアントのためにそれが難しくなります。また

struct my_class { 
    template<typename T, typename U> 
    my_class(T&& x, U&& y) 
    : m_x(std::forward<T>(x)), m_y(std::forward<U>(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

、私は、G ++での警告の束を得るので、私は「wouldnそれをお勧めします。完全性のためにそれを言います。

+0

pass-by-valueは引数のコピーを生成します。これは、私が正確に質問を理解すれば、user1030861が避けたいものです。 – Gabriel

+0

@Gabriel:引数の値のカテゴリに依存します。引数がrvalueの場合、値渡しの呼び出しはコピーではなく引数を移動*します。引数が左辺値の場合にのみコピーが実行されます。 – fredoverflow

+0

@ Gabrial:私の答えをより完全な説明で更新しました。それは役に立ちますか? – fredoverflow

関連する問題