std :: вектор объектов и константность - PullRequest
14 голосов
/ 08 ноября 2010

Рассмотрим следующее:

class A {
public:
    const int c; // must not be modified!

    A(int _c)
    :   c(_c)
    {
        // Nothing here
    }

    A(const A& copy)
    : c(copy.c)
    {
        // Nothing here
    }    
};



int main(int argc, char *argv[])
{
    A foo(1337);

    vector<A> vec;
    vec.push_back(foo); // <-- compile error!

    return 0;
}

Очевидно, что конструктора копирования недостаточно. Чего мне не хватает?

EDIT: Ofc. Я не могу изменить this-> c в методе operator = (), поэтому не вижу, как будет использоваться operator = () (хотя это требуется для std :: vector).

Ответы [ 9 ]

17 голосов
/ 09 ноября 2010

Я не уверен, почему никто не сказал этого, но правильный ответ - сбросить const или сохранить A* в векторе (используя соответствующий умный указатель).

Вы можете придать своему классу ужасную семантику, вызвав «копирование», вызывая UB или ничего не делая (и, следовательно, не будучи копией), но почему все эти проблемы танцуют вокруг UB и плохого кода? Что вы получаете, делая это const? (Подсказка: ничего.) Ваша проблема концептуальна: Если у класса есть член const, класс является const. Объекты, которые являются const, принципиально не могут быть назначены.

Просто сделайте его неконстантным private и покажите его значение неизменным. Для пользователей это эквивалентно, по-постоянному. Это позволяет неявно сгенерированным функциям работать очень хорошо.

15 голосов
/ 08 ноября 2010

Элемент контейнера STL должен быть конструируемым и назначаемым для копирования 1 (а ваш класс A - нет) Вам нужно перегрузить operator =.

1 : §23.1 говорит The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignabletypes


РЕДАКТИРОВАТЬ :

Отказ от ответственности : Я не уверен, является ли следующий фрагмент кода безопасным на 100%. Если это вызывает UB или что-то еще, пожалуйста, дайте мне знать.

A& operator=(const A& assign)
{
    *const_cast<int*> (&c)= assign.c;
    return *this;
}

РЕДАКТИРОВАТЬ 2

Я думаю, что приведенный выше фрагмент кода вызывает неопределенное поведение, потому что попытка отбросить константу const квалифицированной переменной вызывает UB.

7 голосов
/ 08 ноября 2010

Вам не хватает оператора присваивания (или оператора копирования), одного из большой тройки .

2 голосов
/ 08 ноября 2010

Сохраненный тип должен соответствовать требованиям CopyConstructible и Assignable , что означает, что operator = также необходим.

1 голос
/ 08 ноября 2010

Вероятно, оператор присваивания .Компилятор обычно генерирует для вас заданный по умолчанию, но эта функция отключена, поскольку у вашего класса нетривиальная семантика копирования.

0 голосов
/ 09 марта 2015

Недавно я столкнулся с той же ситуацией и вместо этого использовал std :: set, потому что его механизм добавления элемента (insert) не требует оператора = (использует оператор <), в отличие от векторного механизма (push_back).</p>

Если производительность является проблемой, вы можете попробовать unordered_set или что-то подобное.

0 голосов
/ 08 ноября 2010

Обходной путь без const_cast.

A& operator=(const A& right) 
{ 
    if (this == &right) return *this; 
    this->~A();
    new (this) A(right);
    return *this; 
} 
0 голосов
/ 08 ноября 2010

Вам также необходимо реализовать конструктор копирования, который будет выглядеть следующим образом:

class A {
public:
    const int c; // must not be modified!

    A(int _c)
    ...

    A(const A& copy)
    ...  

    A& operator=(const A& rhs)
    {
        int * p_writable_c = const_cast<int *>(&c);
        *p_writable_c = rhs.c;
        return *this;
    }

};

Специальный шаблон const_cast принимает тип указателя и возвращает его в форму для записи, напримерthis.

Следует отметить, что const_cast не всегда безопасно использовать, см. здесь .

0 голосов
/ 08 ноября 2010

Я думаю, что для реализации используемой векторной функции в STL требуется оператор присваивания (см. Цитату Прасуна из Стандарта). Однако согласно приведенной ниже цитате, поскольку оператор присваивания в вашем коде неявно определен (так как он не определен явно), ваша программа имеет неправильную форму из-за того, что ваш класс также имеет постоянный нестатический элемент данных. *

C ++ 03

$ 12,8 / 12 - «Неявно объявленный оператор присваивания копии неявно определяется, когда объект своего класса типу присваивается значение его класса тип или значение типа класса происходит от его типа класса. Программа плохо информирован, если класс, для которого оператор присваивания копии неявно определено имеет:

- нестатический элемент данных типа const или

- нестатические данные элемент ссылочного типа или

- а нестатический член данных типа класса (или их массив) с оператор присваивания недоступной копии, или

- базовый класс с недоступным оператор присвоения копии.

...