ぬうぱんの備忘録

技術系のメモとかいろいろ

作った曲一覧はこちら

引数のconst参照と値渡しの自動切り替え

なんの話?

 C++で、サイズの大きいオブジェクトを関数に渡そうと思ったら普通はconstな参照を使うと思います。でも、intとかのプリミティブな型ならわざわざconst参照にしないで素直に値渡しすると思います。この、const参照と値の切り替えをテンプレートな関数とかでどう切り替えよう? というはなしです。

例を考える

こんなような宣言のテンプレート関数を考えましょうか

template<typename T>
void Hoge(const T& val);

引数がconst T&になってますが、これは当然ですね。もしかたら、T型のコピーで凄まじいコストが発生するかもしれませんし、そもそもTがコピー不可であるかもしれません。そういった面倒を避けるという意味では良い事ですね。
ですが、例えば

int Value;
Hoge(Value);

みたいに、わざわざintの変数一個のためにconst参照を作るのはなんとなく勿体ない気がします。そんな大したコストじゃないんだろうなぁという気もしますが、気になりません?

こうなってくると、テンプレート引数として渡された型に応じてconst参照と値を勝手に切り替えてくれる機構が欲しくなってくるわけです。

ついでにもう一例

配列か何か、ランダムアクセスが可能なデータ構造をラッピングしているクラスを考えます。当然、添字演算子による任意要素へのアクセスを実装したくなります。

template<typename T>
class SomeDataStruct{
private:
	...

public:
	//constな添字演算子
	const T& operator[](std::size_t idx) const;

	//constでない場合の添字演算子
	T& operator[](std::size_t idx);
};

本当は戻り値を参照にするのは危ないのですが、参照にすることで

SomeDataStruct<int> Data;
Data[8] = 4;
int Value = Data[8];

みたいなこととか

template<typename T>
void Hoge(const SomeDataStruct<T>& data){
	cout << data[10] << endl;
}

ことが出来てとても便利です。
さて、この場合も値とかなんとかは付きまとってきて

//Tがクラスとかなら
const T& operator[](std::size_t idx) const;
//Tがintとかなら
T operator[](std::size_t idx) const;

みたいに添字演算子オーバーロードしたいわけです。const参照と値を切り替えたいわけですね。ちなみに、constじゃない添字の方はプリミティブ型とかクラスとかそういうの関係なしに参照にするしかありません。

結局のところ

テンプレート引数に応じてconst参照とただの値を適切に切り替えてくれるなんらかの機構が欲しいわけです。
で、型に応じて動作を変えるといえばtraitsで、このくらい自作しても良い気がしますが、どうするのが正解だとか考えてテストするのは面倒・・・。

ということでBoost先生のご登場

この程度のtraits、Boost先生にしっかり入っていて

#include <boost/call_traits.hpp>
boost::call_traits<T>::value_type
boost::call_traits<T>::reference
boost::call_traits<T>::const_reference
boost::call_traits<T>::param_type

っていう感じです。

template<typename T>
void Hoge(typename call_traits<T>::param_type val);

とか

typename call_traits<T>::param_type operator[](std::size_t idx) const;
typename call_traits<T>::reference operator[](std::size_t idx);

って書けばいいってことでしょうか。typenameつけるの面倒っていうんならクラス内でtypedefしてしまえばいいでしょう。

参考

Call Traits - 1.53.0
どんな感じで動作するかはドキュメントに全部書いてあるので、よく解んなくなったらこれ読む感じで。