std::vector<T>::back() を使うと,末尾の要素の参照を取得できる.これは普通は,T&型だ.ところが,T=bool に限っては,「std::_Bit_reference」型なのだ(少なくとも g++ の場合).
通常,bool を関数の引数として渡すとき,わざわざ参照渡ししようとは思わないから(別に効率がよくなるわけではない),意識する必要はないのだが,テンプレート関数やクラスを作る場合には,知っておかないと理解不能なエラーに直面することがある.なので,忘備録も兼ねてメモ.
例 †
#include <vector>
using namespace std;
void Func(bool &x)
{
x= false;
}
int main(int argc, char**argv)
{
vector<bool> vec;
vec.push_back(true);
Func(vec.back());
return 0;
}
これを,コンパイルすると,g++ の場合は,Func(vec.back()); に対して
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
というエラーを吐く. これは,bool 限定だ.bool を double や int に変更すれば,何の問題もなくコンパイルできる.
分析 †
このエラーは,冒頭で述べたとおり,std::vector<bool> の参照型が std::_Bit_reference であることに起因する.std::_Bit_reference 型は bool& に変換することができないから,Func(bool&) の引数として使うとエラーとなるのだ.
(ちゃんと調べていないが,おそらく bit** という名前から,bool の vector はビット演算を使った効率化をしているものと予想される.)
このような実装は,STL の仕様を満たしていないのではないか? と思ってしまうが,std::vector<T> では,要素の値型 std::vector<bool>::value_type と要素の参照型 std::vector<bool>::reference の両方が独立に定義できるようになっているようで,必ずしも std::vector<bool>::value_type& と std::vector<bool>::reference が同じである必要はないようだ.(注:STLの仕様をちゃんと精査して書いていない)
実験 †
上記コードの void Func(bool &x) を
void Func(vector<bool>::reference x)
と変更すれば,コンパイルは通るし,期待通りに動く.
対策 †
ぱっと思いつく対策:
- bool を参照渡しする必要は,通常ないと思われるので,値渡しする.
- テンプレートで関数を共通化したい場合,std::vector<bool> については特殊化する.
- もしくは,std::vector<T> の参照を取得するために T& や const T& を使うのではなく,std::vector<T>::reference や std::vector<T>::const_reference を使う.
(追記)
- 次のような bool のラッパを用いるのもひとつの手だ.
class TBool
{
public:
TBool() : entity_(false) {}
TBool(bool e) : entity_(e) {}
TBool(const TBool &b) : entity_(b.entity_) {}
operator bool&() {return entity_;}
operator const bool&() const {return entity_;}
private:
bool entity_;
};
std::vector<TBool> などと使う.