Как предотвратить специализацию std::vector <bool>

У меня есть шаблонный класс, у которого есть член данных типа std::vector<T>, где T также является параметром моего шаблонного шаблона.

В моем классе шаблона я имею довольно некоторую логику, которая делает это:

T &value = m_vector[index];

Это не похоже на компиляцию, когда T является логическим, потому что оператор [] из std::vector не возвращает ссылку bool, а другой тип.

Некоторые альтернативы (хотя мне они не нравятся):

  • сообщите моим пользователям, что они не должны использовать bool в качестве параметра шаблона
  • имеют специализацию моего класса для bool (но для этого требуется некоторое дублирование кода)

Не существует способа рассказать std::vector не специализироваться на bool?

Ответ 1

Вы просто не можете регулярно менять шаблонный код для T, равного bool, если ваши данные представлены std::vector<bool>, потому что это не контейнер. Как отметил @Mark Ransom, вы можете использовать std::vector<char> вместо этого, например. через такую ​​черту

template<typename T> struct vector_trait { typedef std::vector<T> type; };
template<> struct vector_trait<bool> { typedef std::vector<char> type; };

а затем используйте typename vector_trait<T>::type везде, где вы используете std::vector<T>. Недостатком здесь является то, что вам нужно использовать трансляции для преобразования из char в bool.

Альтернативой, предложенной в вашем собственном ответе, является написать оболочку с неявным преобразованием и конструктором

template<typename T>
class wrapper
{
public:
        wrapper() : value_(T()) {}
        /* explicit */ wrapper(T const& t): value_(t) {}
        /* explicit */ operator T() { return value_; }
private:
        T value_;
};

и использовать std::vector< wrapper<bool> > везде, не имея при этом возможности бросать. Однако есть и недостатки, потому что стандартные последовательности преобразования, содержащие реальные параметры bool, ведут себя по-другому, чем пользовательские преобразования с wrapper<bool> (компилятор может максимально использовать 1 пользовательское преобразование и как можно больше стандартных преобразований). Это означает, что код шаблона с перегрузкой функции может тонко ломаться. Вы могли раскомментировать ключевые слова explicit в приведенном выше коде, но это снова добавляет многословие.

Ответ 2

Используйте std::vector<char> вместо этого.

Ответ 3

Будет ли следующая работа для вас?

template <typename T>
struct anything_but_bool {
    typedef T type;
};

template <>
struct anything_but_bool<bool> {
    typedef char type;
};

template <typename T>
class your_class {
    std::vector<typename anything_but_bool<T>::type> member;
};

Менее легкомысленно имя anything_but_bool должно быть prevent_bool или схожее.

Ответ 4

Вы можете использовать собственный класс прокси для хранения bools.

class Bool
{
  public:
    Bool() = default;
    Bool(bool in) : value(in) {}

    Bool& operator=(bool in) {value = in;}
    operator bool() const& {return value;}

  private:
    bool value;
};

Это может потребовать некоторой настройки для ваших целей, но обычно это то, что я делаю в этих случаях.

Ответ 5

Я нашел еще более элегантное решение, основанное на всех ваших вводах.

Сначала я определяю простой класс, содержащий один элемент. Позвольте называть это wrapperClass:

template <typename T>
class wrapperClass
   {
   public:
      wrapperClass() {}
      wrapperClass(const T&value) : m_value(value) {}
      T m_value;
   };

Теперь я могу определить свой std::vector в моем шаблоном, например:

std::vector<wrapperClass<T>> m_internalVector;

Так как sizeof(WrapperClass<bool>) также равно 1, я ожидаю, что sizeof(WrapperClass<T>) всегда будет равен sizeof(T). Поскольку тип данных теперь больше не bool, специализация не выполняется.

В тех местах, где теперь я получаю элемент из вектора, я просто заменяю

m_internalVector[index]

по

m_internalVector[index].m_value

Но это кажется намного более элегантным, чем использование черт для замены bool на char, а затем с помощью преобразований между char и bool (и, вероятно, переинтерпретировать приведения для преобразования char & в bool &).

Как вы думаете?

Ответ 6

Существуют способы предотвращения специализации vector<bool>: передача пользовательского распределителя.

std::vector<bool, myallocator> realbool; 

В следующей статье приведены некоторые детали: https://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=98

Конечно, это требует, чтобы вы контролировали определения vector, поэтому, вероятно, это не решение для вас. Кроме того, у него также есть некоторые недостатки его собственных...