2017-10-21 11 views
2

私は以下のクラスがあります:私は働くために、次のコードをしたいワイド様々な

class Foo 
{ 
public: 
    // Constructors here 
private: 
    std::vector<X> m_data; // X can be any (unsigned) integer type 
}; 

を:

Foo f0; 
Foo f1(1); // and/or f1({1}) 
Foo f2(1, 2); // and/or f2({1, 2}) 
Foo f3(1, 2, 3); // and/or f3({1, 2, 3}) 
Foo f4(1, 2, 3, 4); // ... and so on 
std::vector<int> vec = {1, 2, 3, 4}; 
Foo f5(vec); 
Foo f6(vec.begin(), vec.end()); 
std::list<std::size_t> list = {1, 2, 3, 4}; 
Foo f7(list); 
Foo f8(list.begin(), list.end()); 
std::any_iterable_container container = {1, 2, 3, 4}; 
Foo f9(container); 
Foo f10(container.begin(), container.end()); 
// PS: I guess I only want containers/iterators that store individual 
// values and not pairs (e.g., I don't want care about std::map 
// because it does not make sense anyway). 

これまでのところ、私はSFINAEを結合しようとしましたが、コンストラクタすべての可能な型、可変的なテンプレートなどでオーバーロードします。私が1つのコンストラクタのケースを修正するたびに、別のものが故障します。また、私が書いたコードは非常に複雑で読みにくいものになっています。しかし、問題はかなりシンプルなようで、間違った方法で近づいていると思います。コードをできるだけシンプルに保ちながら、コンストラクタを書く方法(理想的にはC++ 17)の提案は歓迎する以上のものです。

ありがとうございます。

Foo()がデフォルト(ノンパラメトリック)コンストラクタです:

template <typename X> 
class Foo 
{ 
public: 
    Foo() { }; 

    Foo(initializer_list<int> l) :m_data(l) { }; 

    template<typename container> 
    Foo(container const & c) :m_data(c.begin(), c.end()) {}; 

    template<typename iterator> 
    Foo(iterator begin, iterator end) :m_data(begin, end) { }; 
private: 
    std::vector<X> m_data; 
}; 

+0

私はこの問題は、あなたが信じているほど単純であることを確認していませんそうです。 –

+0

あなたが試したこととそのエラーを表示できますか? –

答えて

1

されるには、以下のように定義されています。

template <class T> 
class Foo 
{ 
public: 
    [..] 

private: 
    std::vector<T> m_data; 
} 

サブタスクにこのタスクを破るのをしてみましょう:

は、私たちはm_dataに記入します

template <class Iterator> 
Foo (Iterator begin, Iterator end, typename Iterator::iterator_category * = 0) 
    : m_data(begin, end); 

イテレータから構築しますbeginおよびendから。

第3のパラメータは、iterator_categoryと宣言するIteratorタイプのみがこのプロトタイプと一致することを確認します。この引数はデフォルト値0を持ち、一度も指定されていないため、テンプレートの控除処理中にのみ使用されます。コンパイラがこれが正しいプロトタイプであるかどうかをチェックするときに、タイプIterator::iterator_categoryが存在しない場合、それはスキップされます。 iterator_categoryはすべての標準イテレータに必須の型であるため、それらのために機能します。私たちは、与えられたコンテナから私たちのm_dataを記入しますコンテナ

template <class Container> 
Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0) 
    : m_data(std::begin(container), std::end(container)); 

から

std::vector<int> vec = {1, 2, 3, 4}; 
Foo<int> f(vec.begin(), vec.end()); 
-- AND -- 
std::list<std::size_t> list = {1, 2, 3, 4}; 
Foo<int> f(list.begin(), list.end()); 

構築物:

このc'torは、次の呼び出しを許可します。 std::beginstd::endを使用して繰り返します。これらは.begin().end()よりも一般的であり、より多くのタイプをサポートしています。プリミティブ配列。注

std::vector<int> vec = {1, 2, 3, 4}; 
Foo<int> f(vec); 
-- AND -- 
std::list<std::size_t> list = {1, 2, 3, 4}; 
Foo<int> f(list); 
-- AND -- 
std::array<int,4> arr = {1, 2, 3, 4}; 
Foo<int> f(arr); 
-- AND -- 
int arr[] = {1, 2, 3, 4}; 
Foo<int> f(arr); 

初期化子リストから

template <class X> 
Foo (std::initializer_list<X> && list) 
    : m_data(std::begin(list), std::end(list)); 

を構築:それは通常の場合だと私たちは、右辺値参照としてリストを取る このc'torは、次の呼び出しが可能になりますまた、Lvaluesからの構築をサポートするためにFoo (const std::initializer_list<X> & list)を追加することもできます。

私たちはm_dataを繰り返しリストに反復して入力します。そして、このc'torがサポートされます。ここでは、引数

template <class ... X> 
Foo (X ... args) { 
    int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... }; 
    static_cast<void>(dummy); 
} 

の可変数から

Foo<int> f1({1}); 
Foo<int> f2({1, 2}); 
Foo<int> f3({1, 2, 3}); 
Foo<int> f4({1, 2, 3, 4}); 

コンストラクタを、コンテナにデータを記入することは少しトリッキーです。パラメータ展開を使用して、各引数を展開してプッシュします。このc'tor私たちは呼び出すことができます:

Foo<int> f1(1); 
Foo<int> f2(1, 2); 
Foo<int> f3(1, 2, 3); 
Foo<int> f4(1, 2, 3, 4); 

クラス全体が

最終結果はかなり良いです:

template <class T> 
class Foo 
{ 
public: 
    Foo() { 
    std::cout << "Default" << std::endl; 
    } 

    template <class ... X> 
    Foo (X ... args) { 
    int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... }; 
    static_cast<void>(dummy); 
    std::cout << "VA-args" << std::endl; 
    } 

    template <class X> 
    Foo (std::initializer_list<X> && list) 
     : m_data(std::begin(list), std::end(list)) { 
    std::cout << "Initializer" << std::endl; 
    } 

    template <class Container> 
    Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0) 
     : m_data(std::begin(container), std::end(container)) { 
    std::cout << "Container" << std::endl; 
    } 

    template <class Iterator> 
    Foo (Iterator first, Iterator last, typename Iterator::iterator_category * = 0) 
     : m_data(first, last) { 
    std::cout << "Iterators" << std::endl; 
    } 

private: 
    std::vector<T> m_data; 
}; 
+0

お返事ありがとうございました。私は 'Foo(最初、最後、)の構文は私をちょっと混乱させる(私はそれを本当に理解していない)ことを認めなければならない。また、 'std :: array'ではうまく動作しないことに気付きました(ただし、質問では' std :: array'は必要ありません)。 'std :: array'のイテレータは単なるポインタなので、これはありますか?ありがとうございました! – AstrOne

+0

@AstrOne、私は第3引数と 'std :: array'とプリミティブ配列の例についていくつかの情報を追加しました。 –

+0

ありがとう、私の友人。すべての答えは、このようなコードがどのように機能するかを理解するのに役立ちました。すべての回答は非常に良いですが、皆様の助けに本当に感謝しています。ありがとうございました。 – AstrOne

2

アイデアは、このようなクラスを定義することです。

Foo(initializer_list<int> l){1, 2, 3}のようなリストを受け入れます。

Foo(container const & c)は、beginendイテレータをサポートする任意のコンテナを受け入れます。

Foo(iterator begin, iterator end)は、クラスをbeginendイテレータで初期化します。

使用法:f1実装する

Foo<int> f0; 
    Foo<int> f1({1}); 
    Foo<int> f2({1, 2}); 
    Foo<int> f3({1, 2, 3}); 
    Foo<int> f4({1, 2, 3, 4}); 
    std::vector<int> vec = {1, 2, 3, 4}; 
    Foo<int> f5(vec); 
    Foo<int> f6(vec.begin(), vec.end()); 
    std::list<size_t> list = {1, 2, 3, 4}; 
    Foo<size_t> f7(list); 
    Foo<size_t> f8(list.begin(), list.end()); 
    set<unsigned> container = {1, 2, 3, 4}; 
    Foo<unsigned> f9(container); 
    Foo<unsigned> f10(container.begin(), container.end()); 
+0

あなたのコードが何をしているかを説明してください。 – Wndrr

3

最も簡単な方法 - このIST(コンテナまたはイテレータではない既知のタイプTの可変個の引数を取るように見える)f4を:

template<typename... Args> 
Foo(T arg, Args... args) {...} 

このコンストラクタは少なくとも1つの引数を取るため、デフォルトのコンストラクタf0ではあいまいさはありません。最初の引数の型がTであるため、次のコンストラクタのあいまいさはありません。

あなたは他のコンテナとは異なるstd::vectorstd::listを扱いたい場合は、引数が与えられたテンプレートのインスタンスであるかどうかを確認するために、一部の特殊なヘルパーテンプレートを作成することができます

template<typename> 
struct is_vector : std::false_type {}; 

template<typename T, typename Allocator> 
struct is_vector<std::vector<T, Allocator>> : std::true_type {}; 

はまた、このようにそれを使用しますstd::vectorstd::listヨーヨーのそれぞれのイテレータ型のテストでは

template<typename T, Constraint = typename std::enable_if<is_vector<typename std::remove_reference<T>::type>::value, void>::type> 
Foo(T arg) {...} 

f5f7を実装します同じ方法でf6f8を実装できます。

あなたはこのようなf9(私は仮定)を実装するためのメンバ関数begin()end()の存在を確認することができます。しかし、あなたが明示的に使用してstd::vectorstd::listために、このコンストラクタを無効にする必要があります

template<typename T> 
Foo(T arg, decltype(arg.begin())* = 0, decltype(arg.end())* = 0) {...} 

をあいまいさを避けるために作成したヘルパーテンプレート。引数がf10を実装するためにいくつかのイテレータであれば

をチェックするには、 std::iterator_traitsを使用することができます。

template<typename T, typename Constraint = typename std::iterator_traits<T>::iterator_category> 
Foo(T begin, T end) {...} 

繰り返しますが、明示的std::vectorstd::listのイテレータ型のために、このコンストラクタを無効にする必要があります。クラスを想定し

+0

お返事いただきありがとうございました。 'template <..stuff..> Foo(T begin、T end)'という署名だけでコンストラクタを定義すると、次のコードは失敗します: 'Foo(1,2)'。コンパイラは、(期待されるもののほかに)追加のエラーを与えます: 'struct std :: iterator_traits 'に 'no iterator_category 'という名前の型がありません。これは 'std :: iterator_traits'がSFINAEに似ていないからですか? – AstrOne

+0

@AstrOneわかりません。 'Foo(T begin、T end)'コンストラクタは、イテレータだけを取るように明示的に設計されています。これは、型 'int'で失敗するはずです。追加のエラーは、 'std :: iterator_traits'が' int'で動作しないために、 'int'型の2つの引数でこの候補コンストラクタを呼び出すことができなかった理由を簡単に説明しています)。 – Knoep

関連する問題