С явно удаленными функциями-членами в C++11 по-прежнему стоит наследовать от некопируемого базового класса?



С явно удаленными функциями-членами в C++11 по-прежнему стоит наследовать от некопируемого базового класса?



Я говорю о трюке, когда вы в частном порядке наследуете базовый класс, который имеет частный или удаленный конструктор копирования и назначение копирования (например,boost::noncopyable).



являются ли преимущества, выдвинутые в этом вопрос все еще применимо к C++11?





Я не понимаю, почему некоторые люди утверждают, что это легче сделайте класс не копируемым в C++11.



В C++03:



private:
MyClass(const MyClass&) {}
MyClass& operator=(const MyClass&) {}


В C++11:



MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;




EDIT:



как указывали многие люди, было ошибкой предоставлять пустые тела (т. е. {}) для частного конструктора копирования и оператора присваивания копии, потому что это позволило бы самому классу вызывать эти операторы без каких-либо ошибок. Я сначала начал не добавлять {}, но столкнулся с некоторыми проблемами компоновщика, которые заставили меня добавить {} по какой-то глупой причине (я не помню обстоятельств). Мне лучше знать. : -)

553   5  

5 ответов:

Ну, вот это:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

все еще технически позволяет MyClass для копирования членами и друзьями. Конечно, эти типы и функции теоретически находятся под вашим контролем, но класс все еще копировать. По крайней мере, с boost::noncopyable и = delete,никто скопировать класса.


Я не понимаю, почему некоторые люди утверждают, что проще сделать класс не копируемым в C++11.

это не так много "проще", как "более легкоусвояемым".

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

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

если вы программист на C++, который прочитал вводный текст на C++, но мало подвержен идиоматическому C++ (т. е.: a много программистов на C++), это... запутанным. Он объявляет конструкторы копирования и операторы присваивания копирования, но они пусты. Так зачем же их вообще объявлять? Да, они private, но это только поднимает больше вопросы: зачем делать их рядовой?

чтобы понять, почему это предотвращает копирование, вы должны понять, что, объявив их частными, вы делаете это так, что не члены/друзья не могут скопировать его. Это не сразу очевидно для новичка. Также нет сообщения об ошибке, которое они получат, когда попытаются скопировать его.

теперь сравните его с версией C++11:

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

что нужно, чтобы понять, что этот класс не может быть скопирован? Ничего больше, чем понимание того, что = delete синтаксические средства. Любая книга, объясняющая правила синтаксиса C++11, расскажет вам, что именно это делает. Эффект этого кода очевиден для неопытного пользователя C++.

что замечательно в этой идиоме, так это то, что она становится идиомой, потому что это самый ясный, самый очевидный способ сказать именно то, что вы имеете в виду.

даже boost::noncopyable требует немного больше мысли. Да, это называется "noncopyable", так это самодокументируемыми. Но если вы никогда не видели его раньше, это вызывает вопросы. Почему вы производите от чего-то, что не может быть скопировано? Почему мои сообщения об ошибках говорят о boost::noncopyableконструктор копирования? Так далее. Опять же, понимание идиомы требует больше умственных усилий.

во-первых, как отмечают другие до меня, вы неправильно поняли идиому, вы объявить как частный и не определение:

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

где возвращаемый тип operator= может быть в основном что угодно. На данный момент, если Вы читаете этот код в заголовке, что это на самом деле означает? Он может быть скопирован только друзьями или он не может быть скопирован никогда? Обратите внимание, что если вы предоставляете определение, как в вашем примере, он утверждает, что Я могу быть скопированы только внутри класса и с друзьями, это отсутствие определения, что переводит его в Я не могу быть скопировал. Но отсутствие определения в заголовке не является синонимом отсутствия определения везде, как это может быть определено в файле cpp.

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

C++11 ничего не меняет, он просто делает документацию явной в коде. В той же строке, в которой вы объявляете конструктор копирования удаленным, вы документируете, что хотите его полностью отключен.

в качестве последнего комментария, функция в C++11 не только о том, чтобы писать noncopyable С меньшим кодом или лучше, но скорее о том, чтобы запретить компилятору генерировать код, который вы не хотите генерировать. Это только одно использование этой функции.

в дополнение к пунктам другие подняли...

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

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

Это более читабельно и позволяет компилятору давать лучшие ошибки.

"удалено" более ясно для читателя, особенно если они просматривают класс. Точно так же компилятор может сказать вам, что вы пытаетесь скопировать неподражаемый тип, вместо того, чтобы дать вам общую ошибку "попытка доступа к частному члену".

но на самом деле, это просто удобный объект.

люди здесь рекомендуют объявлять функции-члены без их определения. Я хотел бы отметить, что такой подход не портативный. Некоторые компиляторы / компоновщики требуют, чтобы при объявлении функции-члена вы также определяли ее, даже если она не используется. Если вы используете только VC++, GCC, clang, то вы можете уйти с этим, но если вы пытаетесь написать действительно портативный код, то некоторые другие компиляторы (например, Green Hills) потерпят неудачу.

Comments

    Ничего не найдено.