Оптимизация закона де Моргана с перегруженными операторами

каждый программист должен знать, что:

De Morgan 1
De Morgan 2
(законы де Моргана)

при некоторых обстоятельствах для оптимизации программы может случиться так, что компилятор изменит (!p && !q) до (!(p || q)).

два выражения эквивалентны, и это не имеет никакого значения, оценивая первый или второй.
Но в C++ можно перегружать операторы, и перегруженный оператор не всегда может уважать это свойство. Таким образом, преобразование кода таким образом фактически изменит код.

должен ли компилятор использовать законы де Моргана, Когда !, || и && перегружаются?

8 ответов


внимание:

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

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

источник:http://en.cppreference.com/w/cpp/language/operator_logical (ударение мое)

и вот:

Если есть написанный пользователем кандидат с тем же именем и типы параметров, а встроенная функция оператора кандидата,встроенная функция оператора скрыта и не входит в комплект функции кандидата.

источник: N4431 13.6 встроенные операторы [over.built] (акцент мой)

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

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


Я думаю, что вы сами ответили на свой вопрос: нет, компилятор не может сделать этого. Не только операторы могут быть перегружены, некоторые даже не могут быть определены. Например, вы можете иметь operator && и operator ! определены, а operator || не определен вообще.

обратите внимание, что есть много других законов, которым компилятор не может следовать. Например, он не может изменить p||q to q||p, а также x+y to y+x.

(все вышесказанное относится к перегруженным операторам, как вот чего требует вопрос.)


нет, в этом случае преобразование будет недействительным. Разрешение на преобразование !p && !q на !(p || q) неявно, по правилу as-if. Правило as-if допускает любое преобразование, которое, грубо говоря, не может наблюдаться правильной программой. При использовании перегруженных операторов и обнаружении преобразования это автоматически означает, что преобразование больше не разрешено.


перегруженные операторы per se - это просто синтаксический сахар для вызова функции; сам компилятор не может делать никаких предположений о свойствах, которые могут или не могут выполняться для таких вызовов. Оптимизации, использующие свойства некоторого конкретного оператора (например, де Моргана для булевых операторов, коммутативность для сумм, распределенность для суммы/произведения, преобразование интегрального деления в соответствующее умножение,...) может использоваться только тогда, когда используются "реальные операторы".

обратите внимание, что некоторые части стандартная библиотека может связывать определенное семантическое значение с перегруженными операторами-например, std::sort по умолчанию ожидает operator< Это соответствует строгому слабому порядку между элементами - но это, конечно, указано в предварительных требованиях каждого алгоритма / контейнера.

(кстати, перегружая && и || вероятно, следует избегать в любом случае, так как они теряют свои свойства короткого замыкания при перегрузке, поэтому их поведение становится удивительным и, следовательно, потенциально опасным)


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

ответ: конечно нет!

  • там, где законы де Моргана применяются, они могут быть применены.
  • где они не делают, они не могут.

Это действительно просто.


не напрямую.

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

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

компилятор будет делать оптимизацию по-своему. Возможно, это может привести к идентичности de Morgan, но не на уровне if условие замены.


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


но в C++ можно перегружать операторы, и перегруженный оператор не всегда может уважать это свойство.

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

class Boolean
{
  bool value;

  ..

  Boolean operator||(const Boolean& b)
  {
      Boolean c;
      c.value = this->value || b.value;
      return c;
  }

  Boolean logical_or(const Boolean& b)
  {
      Boolean c;
      c.value = this->value || b.value;
      return c;
  }
}

Итак, эта строка кода

Boolean a (true);
Boolean b (false);

Boolean c = a || b;

эквивалентной

Boolean c = a.logical_or(b);