В чем смысл итераторов constexpr end istream (sentinel)?

N2976 предложил добавить constexpr в некоторые места в стандартной библиотеке. Он отмечает, что iostreams не подходят для constexpr ЗА ИСКЛЮЧЕНИЕМ конечных итераторов. Таким образом, istream_iterator и istreambuf_iterator получили constexpr конструкторы по умолчанию, и на этом все. Например, вы можете увидеть в реализации libstdc++ что constexpr появляется только один раз во всем файле. LWG, вызвавшая это изменение, называлась #1129. В нем говорится:

istream_iterator и istreambuf_iterator должны поддерживать буквальные контрольные значения. Конструктор по умолчанию часто используется для завершения диапазонов и может легко быть буквальным значением для istreambuf_iterator и istream_iterator при повторении типов значений. [Остальные опущены]

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

N3308 — еще один документ, в котором упоминается, но не объясняет проблему:

Некоторые из конструкторов istream_iterator<T> должны быть constexpr, если T является буквальным типом. Цель состоит в том, чтобы позволить существующему методу реализации сохранения встроенного типа T продолжать работать. [libstdc++ делает это, _Tp _M_value] Однако на самом деле это исключает эту технику: конструкторы по умолчанию и конструкторы копирования T не должны быть помечены constexpr, а если это не так, конструкторы istream_iterator<T> не могут быть созданы как constexpr.

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

Кроме того, тестируя GCC 5.2.0 онлайн, я скопировал реализацию libstdc++. Единственное изменение — я удалил constexpr из istream_iterator(). В обоих случаях сборки идентичны.

С constexpr

Без constexpr


person user5353075    schedule 19.09.2015    source источник
comment
Что непонятно? Созданный по умолчанию итератор потока часто используется в качестве конечного итератора, например while (iter != istream_iterator()). Имея это как constexpr, мы могли бы сэкономить наносекунду или две в цикле.   -  person Bo Persson    schedule 19.09.2015
comment
@BoPersson Как выглядит выражение constexpr, если только конечный итератор может быть constexpr? (Кроме того, я не верю, что сэкономленные нам наносекунды или две оправдали бы дефект, статью, а затем разработчиков библиотек, которые думают, что это стоит времени на разработку)   -  person user5353075    schedule 19.09.2015
comment
Я предполагаю, что «движущийся» итератор должен вызывать живые данные, чтобы его состояние не могло быть определено во время компиляции. Но завершающий итератор может быть полностью статичным и полностью выведенным во время компиляции, потому что все, что ему нужно сделать, это сравнить equal с «движущимся» итератором, у которого закончились данные.   -  person Galik    schedule 19.09.2015
comment
Выражение не constexpr (и это в любом случае не будет работать как условие цикла), но сравнение iter с константой может быть дешевле, чем сравнение с чем-то другим.   -  person Bo Persson    schedule 19.09.2015
comment
@BoPersson, разве не constexpr разрешить постоянную инициализацию на этапе статической инициализации? можно ли статически инициализировать временные локальные объекты с нестатической продолжительностью хранения?   -  person Piotr Skotnicki    schedule 19.09.2015
comment
@Пётр - 1. Да. 2. Иногда. Согласно правилу «как если бы», если компилятор может понять, что мы не видим разницы, он может сделать что угодно. Создание конструктора constexpr просто может помочь компилятору с расчетной частью. В данном конкретном случае, я полагаю, большинство оптимизаторов заметили, что istream_iterator() создает константу еще до того, как она была преобразована в constexpr. Так что это не важное изменение в библиотеке.   -  person Bo Persson    schedule 19.09.2015
comment
@BoPersson Я копирую/вставляю реализацию libstdc++ в coliru. Удаление constexpr приведет к созданию идентичных сборок. Я снова не убежден, что эта микрооптимизация является истинной причиной.   -  person user5353075    schedule 19.09.2015
comment
@user5353075 user5353075 - Я думаю, что это изменение в библиотеке, почему бы и нет? Когда был представлен constexpr, кто-то создал в стандартной библиотеке список вещей, которые можно сделать constexpr. В этом случае изменение чрезвычайно просто сделать, оно не нарушает работу старого кода, а если оно каким-то образом влияет на производительность, то, по крайней мере, не отрицательно. Так почему бы не?   -  person Bo Persson    schedule 19.09.2015
comment
@PiotrSkotnicki Создание std::mutex constexpr решит статическую инициализацию (#828) проблема, но это единственная проблема, перечисленная в N2976, которая упоминает статическую инициализацию. Я считаю, что проблема связана с созданием istream_iterator буквального типа, как и большинство других проблем. Однако у меня возникли проблемы с соединением точек.   -  person user5353075    schedule 19.09.2015
comment
@PiotrSkotnicki Если T является буквальным типом, istream_iterator является литералом. У него будет тривиальный конструктор копирования и деструктор.   -  person user5353075    schedule 19.09.2015
comment
@user5353075 user5353075 пометить экземпляр конечного дозорного объекта как статический или thread_local и посмотреть, влияет ли constexpr на ассемблерный код   -  person Piotr Skotnicki    schedule 19.09.2015
comment
@PiotrSkotnicki Да, но незначительно   -  person user5353075    schedule 19.09.2015
comment
@user5353075 Я получил этот результат на основе это и это   -  person Piotr Skotnicki    schedule 19.09.2015
comment
@PiotrSkotnicki А, хорошо. В обоих случаях у меня был istream_iterator constexpr.   -  person user5353075    schedule 19.09.2015


Ответы (1)


Пример использования итератора конца потока в качестве контрольного значения приведен здесь:

// istream_iterator example
#include <iostream>     // std::cin, std::cout
#include <iterator>     // std::istream_iterator

int main () {
  double value1, value2;
  std::cout << "Please, insert two values: ";

  std::istream_iterator<double> eos;              // end-of-stream iterator
  std::istream_iterator<double> iit (std::cin);   // stdin iterator

  if (iit!=eos) value1=*iit;

  ++iit;
  if (iit!=eos) value2=*iit;

  std::cout << value1 << "*" << value2 << "=" << (value1*value2) << '\n';

  return 0;
}

http://www.cplusplus.com/reference/iterator/istream_iterator/istream_iterator/

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

person Davislor    schedule 19.09.2015
comment
Извините, это не убедительный аргумент. Удаление constexpr приведет к созданию точно такой же сборки. - person user5353075; 19.09.2015
comment
@user5353075 user5353075 Это 1 частный случай 1 реализации. Наличие constexpr гарантировало бы то же самое для всех компиляторов и в общем случае. И если вам нужно просмотреть стандартную библиотеку и выяснить, что можно сделать из constexpr, нет смысла что-то упускать, даже если сегодня это не приносит реальной пользы (для одного компилятора). - person nos; 19.09.2015
comment
@nos не должен eos иметь статическое или потоковое хранение, чтобы иметь гарантию, что инициализация выполняется во время компиляции? - person Piotr Skotnicki; 19.09.2015
comment
Простой пример: while ( iit != istream_iterator<double>() ) - person Davislor; 19.09.2015
comment
@nos Вот clang с libc++ (я удалил некоторые специфические вещи GCC и уточнил имена с помощью std::). Я бы протестировал на MSVC, но VS2013 не поддерживает constexpr. - person user5353075; 19.09.2015
comment
@user5353075 user5353075 Конечно, но я не уверен, что понимаю, почему так интересно, что делает конкретный компилятор сегодня. Если вы хотите сравнить, что делают некоторые компиляторы, я уверен, что вы могли бы также удалить много простых констант и получить ту же сборку. Но и const, и constexpr также взаимодействуют с программистом, а не только с компилятором. - person nos; 19.09.2015
comment
@ user5353075 Также не особенно убедительный пример, поскольку компилятор может просто встроить код istream_iterator; другими словами, он может провести собственный анализ константности выражения. Гораздо убедительнее будет пример сравнения сторонней функции с constexpr и без него. - person MicroVirus; 19.09.2015
comment
@Lorehead Объявление этого constexpr позволяет компилятору сворачивать вызовы, создающие итераторы конца потока, в константы. Можете ли вы предоставить стандартную ссылку, в которой говорится об этом (для объектов продолжительности автоматического хранения)? - person Piotr Skotnicki; 19.09.2015
comment
@Piotr Slotniki Те, что в ОП. Вот что означают буквальные дозорные значения. - person Davislor; 19.09.2015
comment
@Lorehead Я спросил, откуда вы взяли эту информацию о том, что компилятор может выполнять определенные оптимизации для локальных, нестатических, нелокальных временных файлов только тогда, когда у них есть конструктор constexpr? - person Piotr Skotnicki; 19.09.2015
comment
@user5353075 user5353075 Вы ищете реализацию, в которой удаление constexpr препятствует оптимизации компилятора сегодня? У меня нет ресурсов для проверки этого прямо сейчас, но это может повлиять на такие вещи, как может ли итератор конца потока быть аргументом шаблона или быть назначенным constexpr. И частью мотива может быть предотвращение написания реализаторами версий конечного итератора, которые не являются константами; которые ищут что-то в текущей локали, возможно. - person Davislor; 19.09.2015
comment
@Piotr Skotnicki Вы правы в том, что eos в приведенном мной примере не следовало объявлять автоматической, неконстантной локальной переменной. Я сейчас разговариваю по телефону, но заметил, что у g++ есть проблемы с автоматическим встраиванием и свертыванием возвращаемых значений double, поэтому я бы сначала проверил это, чтобы найти реальный пример, где constexpr помогает. - person Davislor; 19.09.2015