Класс, содержащий auto_ptr, хранящийся в векторе - PullRequest
3 голосов
/ 01 апреля 2009

В ответе на Безопасно ли хранить объекты класса, который имеет std :: auto_ptr в качестве переменной-члена в std :: vector? Я заявил, что класс, содержащий auto_ptr, может быть хранится в векторе при условии, что класс имеет определяемый пользователем конструктор копирования .

Было несколько комментариев, предполагавших, что это не так, поэтому этот вопрос является попыткой прояснить проблему. Рассмотрим следующий код:

#include <memory>
#include <vector>
using namespace std;

struct Z {};

struct A {

    A( Z z ) 
        : p( new Z(z) ) {} 

    A( const A & a ) 
        : p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}

    // no assigment op or dtor defined by intent

    auto_ptr <Z> p;
};

int main() {
    vector <A> av;              
    Z z;                    
    A a(z);
    av.push_back( a );      
    av.push_back( A(z) );   
    av.clear();             
}                           

Пожалуйста, изучите вышеизложенное, и в своем ответе укажите где неопределено поведение в значении стандарта C ++ может происходить для этого конкретного класса, используемого таким конкретным способом. Меня не интересует, является ли класс полезным, корректным, сортируемым или как он работает при исключениях.

Обратите также внимание, что это не вопрос правильности создания вектора auto_ptrs - мне хорошо известны проблемы, связанные с этим.

Спасибо всем за ваш вклад в то, что в ретроспектива, вероятно, довольно глупо вопрос. Я думаю, что я слишком много внимания на копии ctor & забыл о назначение. Счастливый победитель моего точки принятия (и баллы означают призы!) составляет лит для типичного исчерпывающее объяснение (извините Earwicker)

Ответы [ 5 ]

4 голосов
/ 01 апреля 2009

Объекты, хранящиеся в контейнерах, должны быть «CopyConstructable», а также «Assignable» (C ++ 2008 23.1 / 3).

Ваш класс пытается справиться с требованием CopyConstructable (хотя я бы поспорил, что оно все еще не соответствует ему - я отредактировал этот аргумент, поскольку он не является обязательным и, как я полагаю, он спорный), но он не имеет отношения Назначаемое требование. Чтобы быть назначаемым (C ++ 2008 23.1 / 4), должно быть верно следующее, где t - это значение T, а u - это значение (возможно, const) T:

t = u возвращает T&, а t эквивалентно u

Стандарт также говорит в примечании (20.4.5 / 3): «auto_ptr не соответствует требованиям CopyConstructible и Assignable для элементов контейнера стандартной библиотеки и, следовательно, создание экземпляра контейнера стандартной библиотеки с auto_ptr приводит к неопределенности поведение. "

Поскольку вы не объявляете или не определяете оператор присваивания, будет предоставлен неявный оператор, использующий оператор присваивания auto_ptr, который определенно делает t не эквивалентным u, не говоря уже о том, что он не будет работать вообще для значений "const T u" (на это указывает ответ Earwicker - я просто указываю точную часть (и) стандарта).

3 голосов
/ 01 апреля 2009

Попытка соединить список мест, что делает пример неопределенным поведением.

#include <memory>
#include <vector>
using namespace std;

struct Z {};

struct A {

    A( Z z ) 
        : p( new Z(z) ) {} 

    A( const A & a ) 
        : p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}

    // no assigment op or dtor defined by intent

    auto_ptr <Z> p;
};

int main() {
    vector <A> av;  
    ...
}

Я проверю строки вплоть до той, где вы создаете экземпляр вектора с вашим типом A. Стандарт должен сказать

В 23.1/3:

Тип объектов, хранящихся в этих компонентах, должен соответствовать требованиям типов CopyConstructible (20.1.3) и дополнительным требованиям назначаемых типов.

В 23.1/4 (выделено мое):

В таблице 64 T - это тип, используемый для создания экземпляра контейнера, t - это значение T, а u - это значение ( возможно const ) T.

+-----------+---------------+---------------------+
|expression |return type    |postcondition        |
+-----------+---------------+---------------------+
|t = u      |T&             |t is equivalent to u |
+-----------+---------------+---------------------+

Таблица 64

В 12.8/10:

Если определение класса явно не объявляет оператор присваивания копии, он объявляется неявно. Неявно объявленный оператор присваивания копии для класса X будет иметь вид

X& X::operator=(const X&)

если

  • каждый прямой базовый класс B из X имеет оператор присваивания копии, параметр которого имеет тип const B &, const volatile B & or B и
  • для всех нестатических членов-данных X, которые имеют тип класса M (или его массив), каждый такой тип класса имеет оператор присваивания копии, параметр которого имеет тип const M &, const volatile M & or M.

В противном случае неявно объявленный оператор присваивания копии будет иметь вид

X& X::operator=(X&)

(обратите внимание на последнее и второе последнее предложение)

В 17.4.3.6/1 and /2:

В некоторых случаях (функции замены, функции-обработчики, операции над типами, используемые для создания экземпляров стандартных шаблонных компонентов библиотеки), стандартная библиотека C ++ зависит от компонентов, предоставляемых программой C ++. Если эти компоненты не соответствуют их требованиям, Стандарт не устанавливает требований к реализации.

В частности, эффекты не определены в следующих случаях:

  • для типов, используемых в качестве аргументов шаблона при создании экземпляра компонента шаблона, если операции над типом не реализуют семантику применимого подпункта Требования (20.1.5, 23.1, 24.1, 26.1). Операции с такими типами могут сообщать об ошибке, вызывая исключение, если не указано иное.

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

3 голосов
/ 01 апреля 2009

Я не думаю, что приведенный выше код обязательно скомпилируется. Конечно, разработчик std::vector может потребовать, чтобы был доступен оператор присваивания, от const A&?

И только что попробовав, он не компилируется в Visual Studio C ++ 2008 с пакетом обновления 1:

binary '=': не найден оператор, который принимает правый операнд типа 'const A' (или нет приемлемого преобразование)

Я предполагаю, что под руководством Херба Саттера контейнерные классы в VC ++ прилагают все усилия, чтобы навязать стандартные требования к их параметрам типа, в частности, чтобы им было трудно использовать auto_ptr с ними. Конечно, они, возможно, вышли за границы, установленные стандартом, но я, похоже, помню, что он требовал истинного назначения, а также создания истинной копии.

Однако в g ++ 3.4.5 он компилируется.

0 голосов
/ 01 апреля 2009

А как насчет следующего?

cout << av[ 0 ] << endl;

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

(Это совсем другое, что ваш оригинальный код хорошо компилируется с g++ -pedantic ... и Comeau, но не VS2005.)

0 голосов
/ 01 апреля 2009

Поскольку обычная семантика auto_ptr может свидетельствовать о том, что право собственности передается во время копирования, я бы предпочел использовать здесь boost::scoped_ptr. Конечно, оператор присваивания отсутствует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...