2016-06-28 6 views
0

私はクラスViewを別のコンテナ(疎行列クラスですが、質問には重要ではない)に表示するために作成しようとしています。クラスをコンテナに表示する

Viewは、コンテナ内の要素の選択に参照(例:std::reference_wrapper)を含める必要があり、これらの要素への参照を返すメソッドと、あるブロックを別のブロックと等しくする代入演算子が必要です。

私は、Viewが参照に加えて値を取ることができるようにしたいと考えています。どちらも、参照で使用される非参照インスタンスとして値から構築され、参照インスタンスの単一要素に値を割り当てます。これまで

コードのアンMVEは次のとおりです。

#include <array> 

template<typename T, size_t size> 
class View 
{ 

    private: 
     std::array<T, size> _values; 

    public: 
     View(const std::array<T, size> & values) 
      : _values{ values } { } 
     // ---------- 
     View<T, size> & operator=(const View<T, size> & other) 
     { 
      for (size_t i = 0; i < size; ++i) { 
       this->get(i) = other.get(i); 
      } 
      return *this; 
     } 
     // ---------- 
     T & get(size_t idx) 
     { 
      return _values.at(idx); 
     } 
     const T & get(size_t idx) const 
     { 
      return _values.at(idx); 
     } 

}; 

このように使用することができます:私はclang++を使用してい

10 2 
1 2 3 13 10 

#include <functional> 
#include <iostream> 

int main() 
{ 
    int values[5] = { 1, 2, 3, 4, 5 }; 

    View<int, 2> v1{ 
     {values[0], values[1]} 
    }; 
    View<std::reference_wrapper<int>, 2> v2{ 
     {values[3], values[4]} 
    }; 

    // WHAT WORKS 

    v1.get(0) = 10; // can assign to the non reference `View<int, size>`, 
        // works as intended 

    v2.get(0) += 9; // can increment through the reference wrappers, 
        // this also works as intended 

    // WHAT THAT DOES NOT WORK 

    // v2 = v1; // nether of these work, as there is no conversion 
    // v1 = v2; // between `View<std::reference_wrapper<int>, size>` 
       // and `View<int, size>`. It is the first expression 
       // that is of most interest 

    // v2.get(1) = 10;  // this doesn't work as the return is a 
          // `std::reference_wrapper<int>`, not a 
          // reference to an `int` 
    v2.get(1).get() = 10; // this works as a work-around to 
          // this problem, but it feels clunky, and it 
          // makes the interface between the two types 
          // different 

    for (size_t i = 0; i < 2; ++i) { 
     std::cout << v1.get(i) << " "; 
    } 
    std::cout << std::endl; 
    for (size_t i = 0; i < 5; ++i) { 
     std::cout << values[i] << " "; 
    } 
    std::cout << std::endl; 
} 

この出力すべきUbuntu 15.10でコンパイルする。


だから具体的には、

  1. はどのようView<T, size>View<std::reference_wrapper<T>, size>が互いに(又は後者に割り当てるべき少なくとも前者)に割り当てることができるようにするために代入演算子を実装しなければなりません。 (View<std::reference_wrapper<T>, size>が第2の過負荷のためView<std::reference_wrapper<std::reference_wrapper<T> >, size>を必要とするように)二つのバージョン

    View<T, size> & operator=(const View<T, size> & other); 
    View<T, size> & operator=(
        const View<std::reference_wrapper<T>, size> & other); 
    

    を作成すると、動作しません。

  2. メソッドを記述すると、とView<std::reference_wrapper<T>, size>の両方で返り値がT &になるようにするにはどうすればよいですか?

は、私は、これは何とかテンプレートを使用することによって達成することができる感覚を持っているが、私はまだ、私は少し迷ってしまいましたので、プログラミングテンプレートするのは非常に新しいです。ここで

+1

'View >'から同じ型に割り当てるときは、参照の値を代入するか、参照自体を代入しますか? – Holt

答えて

2

Tstd::reference_wrapper<T>ためget()リターンT&を作るための方法である:

template <typename T> 
struct get_value_type { 
    using type = T; 
}; 

template <typename T> 
struct get_value_type<std::reference_wrapper<T>> { 
    using type = T; 
}; 

template<typename T, size_t size> 
class View { 
    using value_type = typename get_value_type<T>::type; 

    value_type & get(size_t idx) { 
     return _values.at(idx); 
    } 

    const value_type & get(size_t idx) const { 
     return _values.at(idx); 
    } 
}; 

get_value_typeテンプレートは、私たちは、あなたが単にvalue_typeget()の戻り値の型を変更、Tstd::reference_wrapper<T>の両方からTを得る助け、 std::reference_wrapper<T>は暗黙的にT&に変換されるため、動作します。

今、あなたはvalue_typeへのアクセス権を持っていることを、あなたは2 operator=を作成するためにそれを使用することができます:

View& operator= (const View<value_type, size> & other) { 
    for (size_t i = 0; i < size; ++i) { 
     this->get(i) = other.get(i); 
    } 
    return *this; 
} 

View& operator=(const View<std::reference_wrapper<value_type>, size> & other) { 
    for (size_t i = 0; i < size; ++i) { 
     this->get(i) = other.get(i); 
    } 
    return *this; 
} 

あなたが別のビューから割り当てを許可したい場合(例えばdoubleのビューにintの眺め

template <typename U> 
View<T, size> & operator=(const View<U, size> & other) { 
    for (size_t i = 0; i < size; ++i) { 
     this->get(i) = other.get(i); 
    } 
    return *this; 
} 

ビットオフトップかもしれ小さな加え:)、あなたはテンプレートのバージョンを使用することができますIC代わりstd::array属性を持つのは、次のようにstd::arrayから継承できます。

template<typename T, size_t Size> 
struct View: public std::array<T, Size> { 

    using array_type = std::array<T, Size>; 
    using value_type = typename get_value_type<T>::type; 

    View (std::array<T, Size> const& values) : array_type (values) { } 

    View& operator=(const View<value_type, Size> & other) { 
     for (size_t i = 0; i < Size; ++i) { 
      (*this)[i] = other[i]; 
     } 
     return *this; 
    } 

    View& operator=(const View<std::reference_wrapper<value_type>, Size> & other) { 
     for (size_t i = 0; i < Size; ++i) { 
      (*this)[i] = other[i]; 
     } 
     return *this; 
    } 

    value_type & operator[](size_t idx) { 
     return array_type::operator[](idx); 
    } 


    const value_type & operator[](size_t idx) const { 
     return array_type::operator[](idx); 
    } 

}; 

これは、あなたが何かを再定義することなく、あなたのView上の標準ライブラリから多くのものを使用することができるようになります。

+0

よかった、ありがとう! 'operator =(...)'が提案したようにテンプレート化されても大丈夫だと思います。しかし、私がそれを厳密にする必要があれば 'operator =(const View &)'と 'operator =(const View 、size>&)'のような2つのオーバーロードを定義するだけでは不十分です。それとも別の方法がありますか? – eistaa

+0

@eistaaこれはうまくいくはずです。私は 'get()'のトリックの前に 'operator ='のトリックを実際にやったので、 'operator ='の中で 'value_type'を使うことは考えていませんでした。 – Holt

+0

@eistaaまた、(私の編集した答えの最後を参照してください)、インスタンスを格納する代わりに 'std :: array'を継承することができます。これにより、何かを実装することなく(例えば、範囲ベースのループ、標準アルゴリズムなど)、 'View 'で多くの標準ライブラリを使うことができます。 – Holt

関連する問題