Удаление копировщика и присвоение копии - публичное, приватное или защищенное?

avatar
Sajal
17 марта 2019 в 10:01
3488
5
38

Чтобы сделать объект некопируемым, мы можем явно удалить его конструктор копирования и оператор присваивания копии.

Мой вопрос: где правильно это сделать - в разделе public, private или protected класса? И - имеет ли этот выбор значение?

Источник
NathanOliver
30 января 2017 в 17:34
0

function_name() = delete; является новым для C++11. Если вы хотите поддерживать C++98/03, вы не можете его использовать.

Klaus
17 марта 2019 в 10:11
1

Если вы выбрасываете старую обувь, задумываетесь ли вы о том, где ее хранить?

einpoklum
17 марта 2019 в 11:21
14

@Клаус: Нет, но ты думаешь, куда их бросить...

Ответы (5)

avatar
einpoklum
17 марта 2019 в 10:38
45

где это лучше сделать - в общедоступном, приватном или защищенном разделе класса?

Я бы поместил их в раздел public.

Это связано с тем, что удаление конструктора или оператора присваивания ортогонально их созданию private / protected; и когда они не удаляются, по умолчанию они public. Помещение удалений в один из этих двух разделов кажется мне намеком: «Если бы я не удалил их, я бы сделал их частными/защищенными» — это не то сообщение, которое вы хотите передать в вашем случае.

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

Lightness Races in Orbit
17 марта 2019 в 16:32
10

Именно это. Когда-то мы делали эти вещи приватными специально для того, чтобы запретить людям доступ к ним, но это всегда был взлом и был только потому что мы не могли delete их. Это соображение больше не играет роли. Я не помню, имеет ли диагностика «копирующий конструктор является закрытым» приоритет над диагностикой «копирующий конструктор удален» (я сомневаюсь в этом), но даже если он не меняет уровень доступа, это неправильно. по указанным вами причинам.

aschepler
17 марта 2019 в 21:55
0

@LightnessRacesinOrbit Я знаю, что видел, как некоторые компиляторы выдают обе ошибки, когда функция закрыта и удалена. То, что касается приватного доступа, становится просто лишним шумом.

Lightness Races in Orbit
17 марта 2019 в 22:03
0

@aschepler Fair делает

aschepler
17 марта 2019 в 22:24
0

gcc 7.4, но не gcc 8.1: godbolt.org/z/udzwB2 (так что, я думаю, они улучшили это).

einpoklum
17 марта 2019 в 22:37
0

@aschepler: Хм ... вы переключили вкладки вывода, так что похоже, что 7.4 делает все правильно. В любом случае, спасибо.

aschepler
17 марта 2019 в 22:40
0

Хм. Я думаю, что они были расположены правильно, когда я нажал «Поделиться». Ну что ж.

avatar
Gupta
17 марта 2019 в 21:43
16

Из книги Скотта Мейерса, Effective Modern C++ (статья 10), кажется, что лучше определить их как public:

По соглашению удаленные функции объявляются общедоступными, а не частными. На это есть причина. Когда клиентский код пытается использовать элемент Функция C++ проверяет доступность перед удалением статуса. Когда клиент код пытается использовать удаленную приватную функцию, некоторые компиляторы жалуются только о том, что функция является частной, хотя функция доступность на самом деле не влияет на возможность его использования. Это стоит имея это в виду при пересмотре устаревшего кода для замены частные и неопределенные функции-члены с удаленными, потому что обнародование новых функций, как правило, приводит к лучшей ошибке сообщения.

Кроме того, я считаю, что удаленный конструктор/назначение копии должен быть частью интерфейса класса, который должен быть доступен ВСЕМ пользователям класса. Такая информация не должна храниться в секрете, делая ее частной.

Lightness Races in Orbit
17 марта 2019 в 22:04
3

Мейерс противоречит ответу Рассказчика и моим тестам. Это все еще хороший совет, но я считаю, что рассуждения einpoklum лучше.

Gupta
18 марта 2019 в 08:18
0

@LightnessRacesinOrbit Я проверил VS2013. Сообщение об ошибке отличается от g++ и правильно показывает ошибку. Кроме того, с концептуальной точки зрения, когда класс удаляет ctor/cctor и т. д., класс хочет сказать ВСЕМ своим клиентам, что: «Эй, этот член удален, и вам не разрешено его использовать». . В этом факте нет секрета, чтобы держать его в секрете.

Lightness Races in Orbit
18 марта 2019 в 11:25
0

Согласованный; это рассуждение einpoklum, которое я нахожу превосходным;)

avatar
StoryTeller - Unslander Monica
17 марта 2019 в 10:26
21

Имеет ли какое-либо значение расположение удаленного определения?

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

Таким образом, вы можете поместить это удаленное определение с любой доступностью, какой пожелаете. Я думаю, что большинство будет держать его закрытым, чтобы соответствовать «старой» практике сделать класс некопируемым (поместить объявление этих членов в частный раздел класса, а не определять их), хотя бы для того, чтобы помочь тем кто знает старые способы "получить его" раньше. Смесь идиом, если хотите.

Отметить как частный также нельзя, если вам нужна поддержка режима C++03 и C++11. С помощью макроса можно легко привести заголовок в соответствие с обоими стандартами:

.
#if __cplusplus >= 201103L
  #define DELETED_DEFINITION = delete
#else
  #define DELETED_DEFINITION
#endif

class noncopyable {
private:
  // This header can be compiled as both C++11 and C++03
  noncopyable(noncopyable const&) DELETED_DEFINITION;
  void operator=(noncopyable const&) DELETED_DEFINITION;
};
Lightness Races in Orbit
17 марта 2019 в 16:32
0

Если вам нужна обратная совместимость, то это обязательно.

Gupta
18 марта 2019 в 08:23
2

@LightnessRacesinOrbit Если вам нужна обратная совместимость, НИКОГДА не используйте функции C++11. В случае удаленных функций StoryTeller предлагает обходной путь. Но каково решение обратной совместимости при использовании лямбда-выражений, stl, параллелизма и т. д.?

Lightness Races in Orbit
18 марта 2019 в 11:23
2

@hsalimi Пока вы можете использовать функции C ++ 11, если вам нужен полезный уровень совместимости, правда, но у меня была библиотека среды событий в предыдущем проекте, которую можно было скомпилировать либо на C ++ 03, либо на C + +11 (он по-прежнему использовался в устаревших встроенных проектах), и в последнем случае он имел кучу оптимизаций (в основном связанных с ссылками rvalue), которые значительно улучшили ситуацию, не слишком сильно перегружая интерфейс. Мне действительно нужно было переключаться между Boost.Thread и std::thread, но тогда в последнем случае это облегчает зависимость от библиотеки (и ссылки!), Так что это не напрасно.

Lightness Races in Orbit
18 марта 2019 в 11:24
0

@hsalimi Но да, это означало, что я не мог использовать лямбда-выражения в библиотеке ... или, по крайней мере, это было бы больше проблем, чем того стоило.

avatar
Potatoswatter
30 января 2017 в 17:21
7

delete работает так же хорошо с доступом private.

Эффект delete заключается в том, чтобы вызвать ошибку, если функция выбрана по разрешению перегрузки.

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

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

schoetbi
6 февраля 2018 в 12:03
1

Удаленные функции рекомендуется сделать общедоступными. См. coderhelper.com/a/18931192/108238. Также Clang-Tidy предлагает это.

avatar
Martin Bonner supports Monica
30 января 2017 в 17:20
2

Доступ к функции deleted не имеет значения. Фактически, для членов класса было бы разумнее добавить дополнительный спецификатор доступа (delete:). Я подозреваю, что причина, по которой они этого не сделали, заключалась в том, что это не сработает для функций, не являющихся членами.

Для таких вещей, как конструктор копирования, стилистически более целесообразно поместить его в раздел public. Тот факт, что класс не имеет конструктора копирования, является довольно важным фактом, который необходимо знать об интерфейсе класса.

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