C++11標準ライブラリソース分析:連載の7
9846 ワード
std::tuple
tuple簡史
C++Referenceのtupleに対する解釈は「fixed-size collection of heterogeneous values」,すなわち固定長の異性データの集合である.各C++コード仔がよく知っている
tupleの使い方
C++11標準ライブラリの
しかし、私たちは一般的に
C++11標準ライブラリには、
tupleの実現原理
もしあなたが
tuple_size
まず、補助クラスから始めます.
これは、
tuple_types
次の補助クラスは
この簡略版の
type_indices
次は少し複雑です.
コンパイラは次のように展開します.
したがって、次のコードについては、
コンパイラの最終展示会は次のとおりです.
これにより、
tuple_element
最後の補助クラスは
私は上のコードがまたあなたをめまいがすることを知っているので、詳しく説明します.このようなコードを書くと:
コンパイラコンベンション(煩わしいnamespace限定子を省略した後):
なお、上記のコードでは、クラス
複雑そうに見えるが、文字の置き換えにほかならない.
tuple
はい、お酒の準備ができました.次はメイン料理です.
続行:
見たでしょう、それぞれの
コンパイラコンベンション
脳の穴が開いているような感じがしますか?
make_tupleとget
使いやすいように、標準ライブラリでは、関数
これらのコードは私は説明しないで、あなたに自分で消化させます.
まとめ
本章で示す
tuple簡史
C++Referenceのtupleに対する解釈は「fixed-size collection of heterogeneous values」,すなわち固定長の異性データの集合である.各C++コード仔がよく知っている
std::pair
はtuple
です.ただし、std::pair
は2つのデータしか収容できませんが、C++11標準ライブラリで定義されているtuple
は、任意の複数、任意のタイプのデータを収容できます.tupleの使い方
C++11標準ライブラリの
tuple
はテンプレートクラスで、使用時にヘッダファイル
を含める必要があります.#include
using tuple_type = std::tuple;
tuple_type t1(1, 2.0, 'a');
しかし、私たちは一般的に
std::make_tuple
関数を使用してtuple
を作成します.std::make_tuple
を使用するメリットは、tuple
パラメータのタイプを指定する必要がなく、コンパイラが自分で推定することです.#include
#include
auto t1 = std::make_tuple(1, 2.0, 'a');
std::cout << typeid(t1).name() << std::endl
std::get
関数を使用して、tuple
のデータを取り出すことができます.auto t = std::make_tuple(1, 2.0, 'a');
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << ", " << std::get<2>(t) << std::endl; // 1, 2.0, a
C++11標準ライブラリには、
tuple
クラスの情報を取得するのに便利な補助クラスも定義されています.using tuple_type = std::tuple;
// tuple_size: tuple
cout << std::tuple_size::value << endl; // 3
// tuple_element: tuple
cout << typeid(std::tuple_element<2, tuple_type>::type).name() << endl; // c
tuple
の使い方について簡単に紹介します.C++Referenceではstd::tuple
について詳しく紹介されています.興味のある方は行ってみてください.次に、tuple
の実現原理を再説明します.tupleの実現原理
もしあなたが
boost::tuple
を知っているならば、boost::tuple
は再帰的なネストを使用して実現されていることを知っておくべきで、これも多くのクラスライブラリ--例えばLokiとMS VC--実現tuple
の方法です.libc++
別の道を切り開き、多重継承の手法を採用して実現した.libc++
のtuple
のソースコードは極めて複雑で、メタプログラミング技術を大量に使用しています.もし私が一行でこれらのソースコードを解読すれば、本章はC++テンプレートメタプログラミングの入門になります.あなたが引き続き見る勇気があるために、私はlibc++ tuple
のソースコードを簡略化して、1つの極簡版tuple
を実現して、あなたにtuple
の仕事の原理を理解することを助けることができることを望みます.tuple_size
まず、補助クラスから始めます.
// forward declaration
template class tuple;
template class tuple_size;
// tuple
template
class tuple_size > : public std::integral_constant {};
これは、
tuple_size
が1つのtuple
に作用する場合、tuple_size
の値がsizeof...(T)
の値であることを理解しやすい.このように書くことができますcout << tuple_size >::value << endl; // 3
tuple_types
次の補助クラスは
tuple_types
:template struct tuple_types{};
template::value, size_t Start = 0>
struct make_tuple_types {};
template
struct make_tuple_types, End, 0> {
typedef tuple_types type;
};
template
struct make_tuple_types, End, 0> {
typedef tuple_types type;
};
この簡略版の
typle_types
は具体的なことをしないで、純粋なタイプ定義です.なお、この簡略版のtuple_types
を使用する場合は、End == sizeof...(T) - 1
を保証したほうがいいです.そうしないと、コンパイラがエラーを報告する可能性があります.type_indices
次は少し複雑です.
template struct tuple_indices {};
template
struct integer_sequence {
template
using to_tuple_indices = tuple_indices;
};
template
using make_indices_imp = typename __make_integer_seq::template to_tuple_indices;
template
struct make_tuple_indices {
typedef make_indices_imp type;
};
__make_integer_seq
は、LLVMコンパイラに組み込まれた関数です.その役割は、その名の通りです.コンパイル中にシーケンスを生成します.このようなコードを書くと、次のようになります.__mkae_integer_seq
コンパイラは次のように展開します.
integer_sequence<0>, integer_sequence<1>, integer_sequence<2>
したがって、次のコードについては、
make_tuple_indices<3>
コンパイラの最終展示会は次のとおりです.
tuple_indices<0>, tuple_indices<1>, tuple_indices<2>
これにより、
tuple
のインデックスが定義されます.tuple_element
最後の補助クラスは
tuple_element
:namespace indexer_detail {
template
struct indexed {
using type = T;
};
template struct indexer;
template
struct indexer, tuple_indices > : public indexed... {};
template
indexed at_index(indexed const&);
} // namespace indexer_detail
template
using type_pack_element = typename decltype(indexer_detail::at_index(
indexer_detail::indexer,
typename make_tuple_indices::type>{}))::type;
template
struct tuple_element > {
typedef type_pack_element type;
};
template
struct tuple_element > {
typedef type_pack_element type;
};
私は上のコードがまたあなたをめまいがすることを知っているので、詳しく説明します.このようなコードを書くと:
tuple_element<1, tuple >::type
コンパイラコンベンション(煩わしいnamespace限定子を省略した後):
tuple_pack_element<1, int, double, char>
を開き、さらに展開するdecltype(
at_index<1>(indexer, tuple_indices<3>>{})
)
なお、上記のコードでは、クラス
indexer
を関数at_index
のパラメータとして定義しているが、関数at_index
はat_index
タイプのパラメータのみを受け入れているため、コンパイラはindexer
をindexed<1,double>
に変換し、indexed<1, double>::type
はdouble
に変換する.複雑そうに見えるが、文字の置き換えにほかならない.
tuple
はい、お酒の準備ができました.次はメイン料理です.
template
class tuple_leaf {
Head value;
public:
tuple_leaf() : vlaue(){}
template
explicit tuple_leaf(cosnt T& t) : value(t){}
Head& get(){return value;}
const Head& get() const {return value;}
};
tuple_leaf
はtuple
の基本構成単位であり、各tuple_leaf
にはインデックス(最初のテンプレートパラメータ)が保存され、値もあります.続行:
template struct tuple_imp;
template
struct tuple_imp, T...> :
public tuple_leaf... {
tuple_imp(){}
template
tuple_imp(tuple_indices, tuple_types, U&& ...u)
: tuple_leaf(std::forward(u))... {}
};
template
struct tuple {
typedef tuple_imp::type, T...> base;
base base_;
tuple(const T& ...t)
: base(typename make_tuple_indices::type(),
typename make_tuple_types::type(),
t...){}
};
見たでしょう、それぞれの
tuple
は数のtuple_leaf
から継承されています.前述したように、各tuple_leaf
にはインデックスと値があるので、tuple
を定義するために必要な情報は、これらのtuple_leaf
に保存されます.このようなコードがあればtuple(1, 2.0, 'a')
コンパイラコンベンション
struct tuple_imp : public tuple_leaf<0, int>, // value = 1
public tuple_leaf<1, double> // value = 2.0
public tuple_leaf<2, char> // value = 'a'
脳の穴が開いているような感じがしますか?
make_tupleとget
使いやすいように、標準ライブラリでは、関数
make_tuple
とget
も定義されています.// make_tuple
template
struct make_tuple_return_imp {
typedef T type;
};
template
struct make_tuple_return {
typedef typename make_tuple_return_imp::type>::type type;
};
template
inline tuple::type...> make_tuple::type...>(std::forward(t)...);
}
// get
template
inline typename tuple_element >::type& get(tuple& t) {
typedef typename tuple_element >::type type;
return static_cast&>(t.base_).get();
これらのコードは私は説明しないで、あなたに自分で消化させます.
まとめ
本章で示す
tuple
は簡略化版の例にすぎず、工業強度のtuple
を実現するには、まだ多くの仕事が必要である.興味のある学生はlibc++
のソースコードを見に行くことができます.