const / nonconstバージョンを備えたC ++のテンプレートクラスのオーバーロード[]オペレーター
-
27-09-2019 - |
質問
ええ、それは長いタイトルでした。
これが私の問題です。 C ++にテンプレートクラスがあり、[]オペレーターにオーバーロードしています。私はconstバージョンと非コンストバージョンの両方を持っています。非コンストバージョンは参照によって返され、クラス内のアイテムをそのように変更できるようにすることができます。
myobject[1] = myvalue;
これはすべて、テンプレートパラメーターとしてブール値を使用するまで機能します。エラーを示す完全な例を次に示します。
#include <string>
#include <vector>
using namespace std;
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}
エラーはコンパイラエラーであり、メッセージは次のとおりです。
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
私は読み上げて、STLがいくつかの一時的なデータ型を使用していることを発見しましたが、ブール以外のすべてで動作する理由がわかりません。
これについての助けがいただければ幸いです。
解決
なぜなら vector<bool>
STLに特化しており、実際には標準容器の要件を満たしていません。
Herb SutterはGOTWの記事でそれについてもっと話します: http://www.gotw.ca/gotw/050.htm
他のヒント
a vector<bool>
本物の容器ではありません。あなたのコードは、事実上、参照を1つのビットに戻そうとしていますが、これは許可されていません。コンテナをaに変更した場合 deque
, 、あなたが期待する行動を得ると思います。
a vector<bool>
他のすべてのベクトルと同様に実装されておらず、それらのようにも機能しません。単に使用しないでください。コードがその多くの特性を処理できないかどうかを心配しないでください - それはほとんどが悪いことであると考えられています。
クラスのモノラルの変更が修正するはずです。
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
// This works better if you pass by const reference.
// This allows the compiler to form temorary objects and pass them to the method.
void add(T const& item)
{
_items.push_back(item);
}
// For the const version of operator[] you were returning by value.
// Normally I would have returned by const ref.
// In normal situations the result of operator[] is T& or T const&
// But in the case of vector<bool> it is special
// (because apparently we want to pack a bool vector)
// But technically the return type from vector is `reference` (not T&)
// so it you use that it should compensate for the odd behavior of vector<bool>
// Of course const version is `const_reference`
typename vector<T>::const_reference operator[](int idx) const
{
return _items[idx];
}
typename vector<T>::reference operator[](int idx)
{
return _items[idx];
}
};
他の回答が指摘しているように、Vector <bool>の場合、スペース割り当てを最適化するための専門化が提供されます。
ただし、T&の代わりにVector ::参照を使用すると、コードを有効にすることができます。実際、STLコンテナが保持しているデータを参照する場合、コンテナ::参照を使用することをお勧めします。
T& operator[](int idx)
なります
typename vector<T>::reference operator[](int idx)
もちろん、constリファレンスのtypedefもあります。
const T operator[](int idx) const
そして、これは(役に立たない余分なコピーを削除する)になります
typename vector<T>::const_reference operator[](int idx) const
エラーの理由はそれです vector<bool>
内部に保存されているブール値を梱包するために特化しています vector<bool>::operator[]
値にアクセスできるようなある種のプロキシを返します。
私は解決策が同じタイプを返すことだとは思わない vector<bool>::operator[]
なぜなら、あなたはあなたの容器に残念な特別な行動をコピーするだけだからです。
使い続けたい場合 vector
基礎となるタイプとして、私はブールの問題を使用してパッチを当てることができると思います vector<MyBool>
代わりに MyClass
にインスタンス化されます bool
.
このように見えるかもしれません:
#include <string>
#include <vector>
using namespace std;
namespace detail
{
struct FixForBool
{
bool value;
FixForBool(bool b): value(b) {}
operator bool&() { return value; }
operator const bool& () const { return value; }
};
template <class T>
struct FixForValueTypeSelection
{
typedef T type;
};
template <>
struct FixForValueTypeSelection<bool>
{
typedef FixForBool type;
};
}
template <class T>
class MyClass
{
private:
vector<typename detail::FixForValueTypeSelection<T>::type> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}