10

N2976 suggested adding constexpr to some spots in the standard library. It notes that iostreams are inappropriate for constexpr EXCEPT end iterators. So istream_iterator and istreambuf_iterator were given constexpr default constructors and that's about it. For example, you can see in the libstdc++ implementation that constexpr only appears once in the entire file. The LWG that sparked this change was #1129. It says:

istream_iterator and istreambuf_iterator should support literal sentinel values. The default constructor is frequently used to terminate ranges, and could easily be a literal value for istreambuf_iterator, and istream_iterator when iterating value types. [Rest omitted]

This doesn't make a whole lot of sense to me. Can someone show me an example of what they mean?

N3308 is another paper that mentions but doesn't explain the issue:

Some of the istream_iterator<T> constructors are required to be constexpr if T is a literal type. The intention is to allow existing implementation technique of storing a type of T inline to continue to work. [libstdc++ does this, _Tp _M_value] However, it actually rules out this technique: the default and copy constructors of T need not be marked constexpr, and if they are not, the istream_iterator<T> constructors could not be instantiated as constexpr.

The above explains the trivial copy constructor and destructor, but not why the default constructor is marked constexpr.

Furthermore, testing on online GCC 5.2.0, I copied libstdc++'s implementation. The only change is I removed constexpr from istream_iterator(). In both cases, the assemblies are identical.

With constexpr

Without constexpr

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
user5353075
  • 101
  • 3
  • 2
    What isn't clear? A default constructed stream iterator is often used as the end iterator, like `while (iter != istream_iterator())`. Having that as a `constexpr` might save us a nanosecond or two in the loop. – Bo Persson Sep 19 '15 at 08:12
  • @BoPersson How is the expression `constexpr` if only the end iterator can be `constexpr`? (Also I don't believe "save us a nanosecond or two" would justify a defect, a paper, and then library implementators thinking it's worth development time) – user5353075 Sep 19 '15 at 08:20
  • I suppose a 'moving' iterator needs to make calls to live data so its state can't be determined at compile time. But a *terminating* iterator can be completely static and entirely deduced at compile time because all it has to do is compare *equal* with a 'moving' iterator that ran out of data.. – Galik Sep 19 '15 at 08:22
  • The expression isn't `constexpr` (and that wouldn't work as a loop condition anyway), but comparing `iter` to a constant might be cheaper than comparing to something else. – Bo Persson Sep 19 '15 at 08:23
  • @BoPersson isn't it `constexpr` to allow constant initialization in a static initialization phase? can non-static-storage-duration temporary local objects be statically initialized? – Piotr Skotnicki Sep 19 '15 at 08:26
  • @Piotr - 1. Yes. 2. Sometimes. Under the "as-if"-rule, if the compiler can figure out that we don't see the difference, it can do anything. Making a constructor `constexpr` just *might* help the compiler with the figure-out-part. In this particular case, I believe most optimizers noticed that `istream_iterator()` produced a constant, even before it was made `constexpr`. So it is not an important change in the library. – Bo Persson Sep 19 '15 at 08:40
  • @BoPersson I copy/pasted libstdc++'s implementation into [coliru](http://coliru.stacked-crooked.com/a/c1dcd7a9875d17e7). Removing `constexpr` will produce identical assemblies. Again I remain unconvinced that this microoptimization is the true reason. – user5353075 Sep 19 '15 at 08:45
  • @user5353075 - I think this is a "Why not?"-change to the library. When `constexpr` was introduced, someone produced a list of things in the standard library that *could* be made `constexpr`. In this case, the change is extremely easy to do, doesn't break any old code, and if it has *any* effect on performance, at least it will not be negative. So, why not? – Bo Persson Sep 19 '15 at 09:20
  • @PiotrSkotnicki Making `std::mutex` constexpr will solve the [static initialization (#828)](https://lwg.github.io/issues/lwg-defects.html#828) issue, but that's the only issue listed in N2976 that mentions static initialization. I believe the issue is related to making `istream_iterator` a literal type, like most of the other issues. However I'm having trouble connecting the dots. – user5353075 Sep 19 '15 at 09:33
  • @PiotrSkotnicki If T is a literal type, istream_iterator *is* a literal type. It'll have a trivial copy constructor and destructor. – user5353075 Sep 19 '15 at 09:52
  • @user5353075 mark an instance of an end sentinel object as static or thread_local and see if constexpr makes difference in assembly code – Piotr Skotnicki Sep 19 '15 at 09:53
  • @PiotrSkotnicki [It did, but minor](https://www.diffchecker.com/w8ixhqrd) – user5353075 Sep 19 '15 at 09:58
  • @user5353075 I got [this result](https://www.diffchecker.com/p2wz1ucc), based on [this](http://coliru.stacked-crooked.com/a/402547aa64f7cd4d) and [that](http://coliru.stacked-crooked.com/a/420f6c748f9dee08) – Piotr Skotnicki Sep 19 '15 at 10:11
  • @PiotrSkotnicki Ah okay. I had `istream_iterator` constexpr in both cases. – user5353075 Sep 19 '15 at 10:21

1 Answers1

3

An example of an-end of stream iterator being used as a sentinel value is here:

// 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/

Declaring this a constexpr allows the compiler to fold calls that create end-of-stream iterators into constants, rather than calling a function each time. It might otherwise have to do so on each iteration of a loop.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Davislor
  • 14,674
  • 2
  • 34
  • 49
  • Sorry, this isn't a convincing argument. [Removing constexpr](http://coliru.stacked-crooked.com/a/c1dcd7a9875d17e7) will produce the exact same assembly. – user5353075 Sep 19 '15 at 08:43
  • 1
    @user5353075 That's 1 particular case of 1 implementation. Having it constexpr would guarantee the same over all compilers and in the general case. And if you're on the task to go over the standard library and figure out what could be made constexpr, it doesn't make sense to omit something, even if has no real benefit (on one compiler) today. – nos Sep 19 '15 at 09:05
  • @nos shouldn't `eos` have static or thread storage duration to have a *guarantee* that initialization is performed at compile time? – Piotr Skotnicki Sep 19 '15 at 09:07
  • Simple example: `while ( iit != istream_iterator() )` – Davislor Sep 19 '15 at 09:12
  • @nos Here's [clang with libc++](http://coliru.stacked-crooked.com/a/218a7cc076aee0b5) (I removed some GCC specific stuff, and qualified names with `std::`). I'd test on MSVC but VS2013 doesn't support constexpr. – user5353075 Sep 19 '15 at 09:17
  • @user5353075 Sure, but I'm not sure I understand why it is overly interesting what a particular compiler does today. If you want to compare what some compilers do, I'm sure you could remove a whole lot of plain const too, and get the same assembly. But both const and constexpr communicate to the programmer too, not just to the compiler. – nos Sep 19 '15 at 09:23
  • @user5353075 Also not a particularly convincing example, since the compiler can just inline the `istream_iterator` code; in other words, it can do its own analysis of the constant-ness of the expression. Much more convincing would be an example comparing a foreign function with and without `constexpr`. – MicroVirus Sep 19 '15 at 10:33
  • @Lorehead *"Declaring this a constexpr allows the compiler to fold calls that create end-of-stream iterators into constants"* can you provide standard reference that says so (for automatic storage duration objects)? – Piotr Skotnicki Sep 19 '15 at 11:30
  • @Piotr Slotniki The ones in the OP. That's what "literal sentinel values" means. – Davislor Sep 19 '15 at 17:36
  • @Lorehead I asked, where did you get this information from, that a compiler can perform certain optimizations on local, non-static, non-threadlocal temporaries only when they have a `constexpr` constructor? – Piotr Skotnicki Sep 19 '15 at 17:45
  • @user5353075 Is what you're looking for an implementation where removing `constexpr` prevents the compiler from optimizing, today? I don't have the resources to test for that right now, but it might affect things such as whether the end-of-stream iterator can be a template argument or assigned to a constexpr. And part of the motive might be to prevent implementers from writing versions of the end iterator that aren't constants; that look up something in the current locale, perhaps. – Davislor Sep 19 '15 at 17:46
  • @Piotr Skotnicki You're right that `eos` in the example I quoted should not have been declared an automatic, non-const local variable. I'm on my phone right now, but I've noticed that g++ has trouble automatically inlining and folding `double` return values, so that's what I would test first to look for a real-world example where `constexpr` helps. – Davislor Sep 19 '15 at 17:54