std необязательный конструктор копирования - PullRequest
4 голосов
/ 31 октября 2019

Я очень озадачен тем, как конструктор копирования std::optional должен быть реализован в соответствии с требованиями constexpr.

Обратите внимание, что в Stackoverflow есть много других вопросов, задающих нечто подобное,например:

Как реализовать конструктор копирования std :: option?

std :: необязательно реализован как union vs char [] / align_storage

Однако ни один из этих вопросов на самом деле не задает вопрос о КОПИРОВАЛЬНОМ КОНСТРУКТОРЕ. Я специально спрашиваю о конструкторе копирования с сигнатурой функции (от https://en.cppreference.com/w/cpp/utility/optional/optional) следующим образом:

constexpr optional( const optional& other );


Теперь я прочитал достаточно о std::optional, чтобы знатьОсновы. Типичная ошибка, которую делают разработчики, заключается в том, чтобы попытаться реализовать ее с std::aligned_storage. Так как размещение new не может работать в constexpr (по крайней мере, в C ++ 17), это не будет работать. Вместо этого требуется тип объединениябыть использованным, чтобы его можно было построить непосредственно. Что-то вроде:

struct dummy_type {};

union optional_impl
{
  dummy_type m_dummy;
  T m_value;
};

Хорошо, но все же ... Я все еще не понимаю, как мы должны отвечать требованиямреализация конструктора копирования как constexpr. Проблема в том, что в конструкторе копирования нам нужно проверить, истинно ли other.has_value(). Если это так, мы хотим напрямую скопировать *other, в противном случае мы просто хотим инициализировать m_dummy. Но как мы можем выразить это условное решение в constexpr конструкторе копирования?

constexpr optional( const optional& other ) : m_dummy{}
{
  if (other.has_value()) new (&m_value) T(*other); // Wrong! Can't use placement new
}

Единственный способ увидеть эту работу - использовать новое размещение.

Поэтому я проверил некоторыефактические реализации, такие как реализация gccздесь:

https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/optional#L248

И действительно ... они просто используют размещение новых. И фактически, конструктор копирования не является событием constexpr (что, я думаю, является дефектом).

Вот еще одна реализация:

https://github.com/akrzemi1/Optional/blob/master/optional.hpp#L416

И снова, они просто используют размещение новых.

Так как же optional(const optional&) может быть реализован как constexpr? Это дефект в стандарте или что-то?

Ответы [ 2 ]

5 голосов
/ 31 октября 2019

Для C ++ 17 см. [Option.ctor] / 6:

... Если is_trivially_copy_constructible_v<T> равно true, этот конструктор должен быть конструктором constexpr.

В этом случае объединение также будет тривиально копируемым, поэтому проблем не будет.

Во всех других случаях конструктор по-прежнему будет содержать спецификатор constexpr, даже если онне может использоваться в постоянном выражении. Это нормально: шаблон функции (или функция-член шаблона класса) может быть объявлен constexpr, если у него есть хотя бы одно возможное создание, которое можно использовать в константных выражениях. ([dcl.constexpr] / 6)

В C ++ 20 формулировка изменилась благодаря P0602R4 . Тем не менее, я думаю, что это не изменило требования constexpr. Если T тривиально копируемо, то конструктор тривиален, что означает, что он также constexpr. Если T нетривиально копируемо, то в стандарте не говорится, что конструктор должен использоваться в константных выражениях, поэтому такого требования нет.

0 голосов
/ 31 октября 2019

То, что это C ++ 17 (был добавлен std :: option c ++ 17), означает, что constexpr будет применяться к функции только в том случае, если аргументы имеют время компиляции.

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

В противном случае она будет вести себя как любой другой экземпляр ctor.

...