Значение = удалить после объявления функции


242
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

Что = deleteзначит в этом контексте?

Есть ли другие "модификаторы" (кроме = 0и = delete)?


23
@Blindy Это будет стандартно в C ++ 0x, т.е. скоро.
Конрад Рудольф

1
Я исправлен, я пропустил эту функцию C ++ 0x. Я думал, что это #defineа-ля Qt, который оценивает до 0, а затем объявляет скрытую функцию или что-то в этом роде.
Blindy

Я вспомнил ключевое слово «disable», которое означает то же самое или что-то подобное. Я воображаю это? Или между ними есть небольшая разница?
Стюарт

Ответы:


201

Удаление функции - это функция C ++ 11 :

Общую идиому «запрещения копирования» теперь можно выразить прямо:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

Механизм «удаления» можно использовать для любой функции. Например, мы можем устранить нежелательные преобразования следующим образом:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};

3
Разве не является традиционным методом "запретить копирование" просто сделать copy-ctor и operator = "private"? Это идет немного дальше и инструктирует компилятор даже не генерировать функции. Если они оба закрытые и = delete, копирование запрещено вдвойне?
Reb.Cabin

8
@Reb, =deleteделает метод недоступным даже из контекстов, которые могут видеть privateметоды (то есть внутри класса и его друзей). Это устраняет любую неопределенность при чтении кода. @Prasoon, во втором примере все еще только удаляются конструкторы - было бы неплохо увидеть, operator long ()например, удаленное .
Тоби Спейт

2
@ Reb.Cabin Использование = deleteлучше, чем использование privateили другие подобные механизмы, потому что обычно вы хотите, чтобы запрещенная функция была визуально объявлена ​​и рассмотрена на предмет разрешения перегрузки и т. Д., Чтобы она могла как можно раньше завершиться сбоем и предоставила пользователю самую очевидную ошибку. Любое решение, которое включает «скрытие» декларации, уменьшает этот эффект.
Леушенко

1
Есть ли особая причина сделать конструктор копирования общедоступным и применить ключевое слово delete. Почему бы не оставить конструктор закрытым и не применить ключевое слово?
Дон Джо

81
  1. = 0означает, что функция является чисто виртуальной, и вы не можете создать экземпляр объекта из этого класса. Вы должны извлечь из него и реализовать этот метод
  2. = deleteозначает, что компилятор не будет генерировать эти конструкторы для вас. AFAIK это разрешено только в конструкторе копирования и операторе присваивания. Но я не слишком хорош в предстоящем стандарте.

4
Есть несколько других применений =deleteсинтаксиса. Например, вы можете использовать его, чтобы явно запретить какие-то неявные преобразования, которые могут иметь место при вызове. Для этого вы просто удалите перегруженные функции. Взгляните на страницу Википедии на C ++ 0x для получения дополнительной информации.
LiKao

Я сделаю это, как только найду. Угадайте, пора догнать c ++ 0X
mkaes

Да, C ++ 0x качается. Я не могу дождаться, когда GCC 4.5+ станет более распространенным, поэтому я могу начать использовать лямбды.
LiKao

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

28

Этот отрывок из языка программирования C ++ [4-е издание] - книга Бьярна Страуструпа рассказывает о реальной цели использования =delete:

3.3.4 Подавление операций

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

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

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

=deleteМеханизм носит общий характер , то есть, он может быть использован для подавления любой операции



5

Стандарты кодирования, с которыми я работал, были следующими для большинства объявлений классов.

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

Если вы используете любой из этих 6, вы просто закомментируете соответствующую строку.

Пример: класс FizzBus требует только dtor, и поэтому не использует остальные 5.

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

Здесь мы закомментируем только 1 и установим его реализацию где-нибудь еще (вероятно, там, где это предполагает стандарт кодирования). Другие 5 (из 6) запрещены с удалением.

Вы также можете использовать '= delete', чтобы запретить неявные рекламные акции разных размеров ... пример

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;

3

= deleteэто функция, представленная в C ++ 11. В соответствии с =deleteэтим не будет разрешено вызывать эту функцию.

В деталях.

Допустим, в классе.

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

При вызове этой функции для назначения объекта она не разрешается. Средство назначения оператора собирается ограничить копирование из одного объекта в другой.


2

Новый стандарт C ++ 0x. Пожалуйста, смотрите раздел 8.4.3 в рабочем проекте N3242


О, этот черновой вариант устарел. Вот самое последнее (по состоянию на 3 апреля 2011 года): open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK

Спасибо и обновил ссылку. Очень полезно получить текущий черновик. Упомянутый раздел / содержание были правильными даже в старом проекте, поэтому я не понимаю отрицательное голосование.
Дабнде

1

Удаленная функция неявно встроена

(Приложение к существующим ответам)

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

Ссылаясь на [dcl.fct.def.delete] / 4 :

Удаленная функция неявно встроена. ( Примечание: правило одного определения ( [basic.def.odr] ) применяется к удаленным определениям. - примечание конца ] Удаленное определение функции должно быть первым объявлением функции или, для явной специализации шаблона функции. первое объявление этой специализации. [Пример:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

- конец примера )

Шаблон основной функции с удаленным определением может быть специализированным

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

До концепции удаленной функции C ++ 11 можно было сделать это, просто опуская определение шаблона первичной функции, но это давало непонятные неопределенные ссылочные ошибки, которые, вероятно, не давали никакого семантического намерения от автора шаблона первичной функции (намеренно опущено ?). Если вместо этого мы явно удалим шаблон первичной функции, сообщения об ошибках в случае, если не найдена подходящая явная специализация, станут намного приятнее, а также покажут, что пропуск / удаление определения шаблона первичной функции было преднамеренным.

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

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

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

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

Возвращаясь к тому, почему мы бы хотели использовать эту технику? Опять же, явные специализации могут быть полезны для неявного удаления неявных преобразований.

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}

0

Это новая вещь в стандартах C ++ 0x, где вы можете удалить унаследованную функцию.


11
Вы можете удалить любую функцию. Например, void foo(int); template <class T> void foo(T) = delete;останавливает все неявные преобразования. intПринимаются только аргументы типа, все остальные будут пытаться создать экземпляр «удаленной» функции.
UncleBens
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.