Неявные специальные функции: когда они будут плохо сформированы?

В Международном стандарте ISO для c++ 11 приводится краткое изложение различий между c++ 2003 и c++ 2011. Одно из отличий:

[Diff.cpp03.special]

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

Обоснование: улучшает сбой вывода аргумента шаблона.

Влияние на оригинальную функцию: действующая программа c++ 2003, которая использует одну из этих специальных функций-членов в контексте, в котором определение не требуется (например, в выражении, которое потенциально не оценивается), становится плохо сформированным.

Я не вижу, в каком состоянии такие специальные функции будут плохо сформированы и как это может сломать SFINAE. Поэтому мой вопрос сводится к следующему:

  • Почему такое изменение "улучшает сбой вывода аргумента шаблона"?
  • Не могли бы вы привести пример?

Ответ 1

struct NonCopyable {
  NonCopyable() {}
private:
  NonCopyable(const NonCopyable &);
};

struct S {
  NonCopyable field;
} s;

int main() {
  return sizeof S(s);
}

Здесь NonCopyable не копируется, но поскольку конструктор копирования явно указан, конструктор неявной копии не создается.

Для S никакой конструктор копирования не предоставляется пользователем, поэтому создается неявный конструктор копирования. Этот конструктор копии NonCopyable field поля NonCopyable, которое не копируется, поэтому конструктор копирования будет плохо сформирован.

В main берется размер объекта S построенного на копирование. Для этого требуется конструктор копирования S, но на самом деле его не называет.

В С++ 03 это действительно. Конструктор копирования будет плохо сформирован, но поскольку никакой копии не было сделано, все в порядке.

В С++ 11 это недопустимо. Конструктор копирования помечен как удаленный, поэтому его нельзя использовать даже в качестве операнда для sizeof.

Это позволяет метапрограммировать, можно ли копировать этот тип, что было невозможно в С++ 03.

Ответ 2

Если вам нужен конкретный пример, std :: pair, как правило, очень легко копировать, но если один из членов, скажем, std :: unique_ptr, unique_ptr не позволяет копировать из Lvalue, значит, он будет плохо сформирован: поэтому операции копирования по умолчанию std :: pair по умолчанию автоматически удаляются.