Whew, that was a long title.
Here's my problem. I've got a template class in C++ and I'm overloading the [] operator. I have both a const and a non-const version, with the non-const version returning by reference so that items in the class can be changed as so:
myobject[1] = myvalue;
This all works until I use a boolean as the template parameter. Here's a full example that shows the error:
#include <string>
#include <vector>
using namespace std;
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
void add(T item开发者_如何学Python)
{
_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;
}
The error is a compiler error and the message is:
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
I've read up and found that STL uses some temporary data types, but I don't understand why it works with everything except a bool.
Any help on this would be appreciated.
Because vector<bool>
is specialized in STL, and does not actually meet the requirements of a standard container.
Herb Sutter talks about it more in a GOTW article: http://www.gotw.ca/gotw/050.htm
A vector<bool>
is not a real container. Your code is effectively trying to return a reference to a single bit, which is not allowed. If you change your container to a deque
, I believe you'll get the behavior you expect.
A vector<bool>
is not implemented like all other vectors, and does not work like them either. You are better off simply not using it, and not worrying if your code can't handle its many peculiarities - it is mostly considered to be A Bad Thing, foisted on us by some unthinking C++ Standard committee members.
Some monor changes to your class should fix it.
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];
}
};
As the other answers point out, a specialization is provided to optimize for space allocation in the case of vector< bool>.
However you can still make your code valid if you make use of vector::reference instead of T&. In fact it is a good practice to use container::reference when referencing data held by a STL container.
T& operator[](int idx)
becomes
typename vector<T>::reference operator[](int idx)
Of course ther is also a typedef for const reference:
const T operator[](int idx) const
and this one becomes (removing the useless extra copy)
typename vector<T>::const_reference operator[](int idx) const
The reason for the error is that vector<bool>
is specialized to pack the boolean values stored within and vector<bool>::operator[]
returns some sort of proxy that lets you access the value.
I don't think a solution would be to return the same type as vector<bool>::operator[]
because then you'd be just copying over the regrettable special behavior to your container.
If you want to keep using vector
as the underlying type, I believe the bool problem could be patched up by using a vector<MyBool>
instead when MyClass
is instantiated with bool
.
It might look like this:
#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;
}
精彩评论