Ответ 1
Поскольку vector<bool>
специализируется на STL и фактически не соответствует требованиям стандартного контейнера.
Herb Sutter говорит об этом больше в статье GOTW: http://www.gotw.ca/gotw/050.htm
Ну, это было длинное название.
Вот моя проблема. У меня есть шаблонный класс на С++, и я перегружаю оператор []. У меня есть как const, так и неконтинентная версия, причем версия non-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 использует некоторые временные типы данных, но я не понимаю, почему он работает со всем, кроме bool.
Любая помощь по этому поводу будет оценена.
Поскольку vector<bool>
специализируется на STL и фактически не соответствует требованиям стандартного контейнера.
Herb Sutter говорит об этом больше в статье GOTW: http://www.gotw.ca/gotw/050.htm
A vector<bool>
не является реальным контейнером. Ваш код эффективно пытается вернуть ссылку на один бит, что недопустимо. Если вы измените свой контейнер на deque
, я верю, что вы получите ожидаемое поведение.
A vector<bool>
не реализуется, как и все другие векторы, и не работает так же, как и они. Вам лучше просто не использовать его, и не беспокоиться о том, не может ли ваш код справиться со своими многочисленными особенностями - в основном это считается "Плохая вещь", навязанная нам некоторыми немыслимыми членами комитета С++ Standard.
Некоторые изменения в вашем классе должны быть исправлены.
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];
}
};
Как указывают другие ответы, предоставляется специализация для оптимизации распределения пространства в случае вектора <BOOL> .
Однако вы все равно можете сделать свой код действительным, если вы используете вектор:: reference вместо T &. На самом деле хорошей практикой является использование контейнера:: reference при обращении к данным, хранящимся в контейнере STL.
T& operator[](int idx)
становится
typename vector<T>::reference operator[](int idx)
Конечно, это также typedef для ссылки const:
const T operator[](int idx) const
и это становится (удаление ненужной дополнительной копии)
typename vector<T>::const_reference operator[](int idx) const
Причиной ошибки является то, что vector<bool>
специализирован для хранения булевых значений, хранящихся внутри, и vector<bool>::operator[]
возвращает некоторый прокси-сервер, который позволяет вам получить доступ к значению.
Я не думаю, что решение будет состоять в том, чтобы вернуть тот же тип, что и vector<bool>::operator[]
, потому что тогда вы просто скопировали бы прискорбное особое поведение вашего контейнера.
Если вы хотите использовать vector
в качестве базового типа, я считаю, что проблема bool может быть исправлена с помощью 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;
}