3

What exactly are the rules for copy elision when one is using auto and commits to a specific type ? (see: GotW - Almost always auto).

From my understanding, the move/copy constructor is required to be accessible even though it's not generally used. But then what's the difference between unique_ptr and fstream in the example below ? (something to do with noexcept ?)

#include <memory>
#include <fstream>

int main()
{
    auto f = std::fstream{"foo.bar"};
    auto i = std::unique_ptr<int>{new int};

    return 0;
}


// main.cc:6:10: error: call to implicitly-deleted copy constructor of 'std::basic_fstream<char>'
//    auto f = std::fstream{"foo.bar"};
//         ^   ~~~~~~~~~~~~~~~~~~~~
3XX0
  • 1,315
  • 1
  • 13
  • 25
  • 4
    There shouldn't be any difference. IIRC this is a libstdc++ bug where it doesn't implement move special members for one of the types that all streams derive from. Here's the [bug report](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316), fixed for gcc5.0 according to JonathanWakely. – Praetorian Feb 25 '15 at 02:15
  • 1
    Also, IMHO, don't take *almost always auto* that literally. Writing `auto f = std::fstream{"foo.bar"};` seems kinda silly. Just use `std::fstream f{"foo.bar"};` – Praetorian Feb 25 '15 at 02:21
  • 1
    `auto` is pointless here. You're naming the type anyway. And there is nothing special about `auto`, it follows the rules of template argument deduction. The one exception where it becomes different is in the case of a deduced `std::initializer_list`, but that isn't present here. –  Feb 25 '15 at 04:21

2 Answers2

3

I guess you use libstdc++. It is currently not standard conformant in this regard, i.e. the iostreams do not have move constructors yet although they should have. It will be fixed in version 5:

Runtime Library (libstdc++)

...

Full support for C++11, including the following new features:

...

movable and swappable iostream classes;

from the version 5 changelog

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
2

auto deduction and copy elision have nothing to do with this; your code is the same as:

std::fstream f = std::fstream{"foo.bar"};
std::unique_ptr<int> i = std::unique_ptr<int>{new int};

In both cases this specifies a copy/move operation which means to use the move constructor if one is available, and to use the copy-constructor otherwise.

As you say, even though the copy/move would be elided in practice, the standard requires that the code conforms to standard requirements without the elision. (This is so that correct code doesn't mysteriously compile and then fail to compile depending on an optimization decision, etc.)

Apparently, C++11 specifies that fstream should have a move constructor but your implementation hasn't actually implemented that yet.

M.M
  • 138,810
  • 21
  • 208
  • 365