Почему std :: swap не помечен как constexpr до C ++ 20? - PullRequest
14 голосов
/ 13 марта 2020

В C ++ 20 std::swap становится функцией constexpr.

Я знаю, что стандартная библиотека действительно отстала от языка в маркировке вещей constexpr, но к 2017 году, <algorithm> был в значительной степени constexpr, как и куча других вещей. Тем не менее - std::swap не было. Я смутно помню, что был какой-то странный дефект языка, препятствующий этой маркировке, но я забыл детали.

Может ли кто-то объяснить это кратко и ясно?

Мотивация: Нужно понять, почему это может быть плохо идея пометить std::swap() -подобную функцию constexpr в коде C ++ 11 / C ++ 14.

Ответы [ 2 ]

11 голосов
/ 13 марта 2020

Странная языковая проблема: CWG 1581 :

В пункте 15 [special] совершенно ясно, что специальные функции-члены определяются неявно только тогда, когда они используются в odr. Это создает проблему для константных выражений в неоцененных контекстах:

struct duration {
  constexpr duration() {}
  constexpr operator int() const { return 0; }
};

// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});

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

Если мы раскомментируем строку # 1, конструктор перемещения будет неявно определена и программа действительна. Это жуткое действие на расстоянии крайне неудачно. Реализации расходятся по этому вопросу.

Вы можете прочитать остальную часть описания проблемы.

Решение для этой проблемы было принято в P0859 в Альбукерке в 2017 году (после C ++ 17 поставляется). Эта проблема была блокирующей, поскольку оба имели возможность иметь constexpr std::swap (решен в P0879 ) и constexpr std::invoke (решен в P1065 , который также имеет примеры CWG1581), оба для C ++ 20.

Самым простым для понимания примером здесь, на мой взгляд, является код из сообщения об ошибке LLVM, указанного в P1065:

template<typename T>
int f(T x)
{
    return x.get();
}

template<typename T>
constexpr int g(T x)
{
    return x.get();
}

int main() {

  // O.K. The body of `f' is not required.
  decltype(f(0)) a;

  // Seems to instantiate the body of `g'
  // and results in an error.
  decltype(g(0)) b;

  return 0;
}

CWG1581 - все о , когда Определены функции-члены constexpr, а разрешение гарантирует, что они определяются только при использовании. После P0859 вышеупомянутое хорошо сформировано (тип b равен int).

Поскольку std::swap и std::invoke оба должны полагаться на проверку функций-членов (переместить конструирование / назначение в первом и вызовы оператора / суррогатные вызовы во втором), они оба зависели от разрешения Эта проблема.

5 голосов
/ 13 марта 2020

Причина

(из-за @NathanOliver)

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

Хронология

  • 2016: Антоний Полухин представляет предложение P0202 , чтобы пометить все функции <algorithm> как constexpr.
  • Основная рабочая группа стандартного комитета обсуждает дефект CWG-1581 . Эта проблема делает проблематичным c иметь constexpr std::swap(), а также constexpr std::invoke() - см. Объяснение выше.
  • 2017: Антоний пересматривает свое предложение несколько раз, чтобы исключить std::swap и некоторые другие конструкции, и это принимается в C ++ 17.
  • 2017: Решение для проблемы CWG-1581 представляется как P0859 и принимается стандартным комитетом в 2017 году (но после C ++ 17 поставляется).
  • Конец 2017 года: Энтони представляет дополнительное предложение P0879 , чтобы сделать std::swap() constexpr после резолюции CWG-1581.
  • 2018: Дополнительное предложение принят (?) в C ++ 20. Как отмечает Барри, исправлено constexpr std::invoke().

Ваш конкретный c case

Вы можете использовать constexpr swapping, если вы не делаете проверка на перемещение-конструируемость и назначение-перемещение, а точнее проверка непосредственно на некоторые другие особенности типов, которые гарантируют это, в частности. например, только примитивные типы и нет классов или структур. Или, теоретически, вы можете предвидеть go проверки и просто иметь дело с любыми ошибками компиляции, с которыми вы можете столкнуться, и с нестабильным переключением поведения между компиляторами. В любом случае, не заменяйте std::swap() на подобные вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...