Sobrecarregado [] operador no modelo de classe em C++ com const / nonconst versões
-
27-09-2019 - |
Pergunta
Ufa, que foi um título longo.
Aqui está o meu problema.Eu tenho um modelo de classes em C++ e estou sobrecarregar o [] operador.Eu tenho um constante e uma não-const versão, com o não-const versão voltar de referência, de modo que os itens da classe pode ser alterada assim:
myobject[1] = myvalue;
Tudo isso funciona até usar um booleano, como o parâmetro do modelo.Aqui está um exemplo completo que mostra o erro:
#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;
}
O erro é um erro de compilador e a mensagem é:
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
Eu já leu e descobriu que STL usa temporários, tipos de dados, mas eu não entendo por que ele funciona com tudo, exceto um bool.
Qualquer ajuda sobre este seria apreciada.
Solução
Porque vector<bool>
é especializado em STL e na verdade não atende aos requisitos de um contêiner padrão.
Herb Sutter fala mais sobre isso em um artigo GOTW: http://www.gotw.ca/gotw/050.htm
Outras dicas
UMA vector<bool>
não é um recipiente real. Seu código está efetivamente tentando retornar uma referência a um único bit, o que não é permitido. Se você mudar seu contêiner para um deque
, Acredito que você terá o comportamento que espera.
Um vector<bool>
não é implementada como todos os outros vetores, e não funciona como eles.É melhor que você simplesmente não o estiver usando, e não se preocupar se o seu código não pode lidar com suas muitas peculiaridades - é principalmente considerado Uma Coisa Ruim, que nos foi impingida por alguns irreflectido, C++ Padrão membros do comitê.
Algumas mudanças de monor na sua classe devem corrigi -la.
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];
}
};
Como as outras respostas apontam, é fornecida uma especialização para otimizar a alocação de espaço no caso do Vector <BOOL>.
No entanto, você ainda pode tornar seu código válido se usar o Vector :: Referência em vez de T &. De fato, é uma boa prática usar o contêiner :: Referência ao referenciar dados mantidos por um contêiner STL.
T& operator[](int idx)
torna-se
typename vector<T>::reference operator[](int idx)
Claro que também é um typedef para const const:
const T operator[](int idx) const
e este se torna (removendo a cópia extra inútil)
typename vector<T>::const_reference operator[](int idx) const
O motivo do erro é que vector<bool>
é especializado para embalar os valores booleanos armazenados dentro e vector<bool>::operator[]
Retorna algum tipo de proxy que permite acessar o valor.
Eu não acho que uma solução seria retornar o mesmo tipo que vector<bool>::operator[]
Porque então você estaria apenas copiando o comportamento especial lamentável do seu contêiner.
Se você quiser continuar usando vector
Como tipo subjacente, acredito que o problema do bool pode ser corrigido usando um vector<MyBool>
Em vez disso, quando MyClass
é instanciado com bool
.
Pode ser assim:
#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;
}