The following code
vector<ofstream> v;
v.emplace_back("file1.txt");
v.emplace_back("file2.txt");
for (int i = 0, ilen = v.size(); i < ilen; ++i)
v[i] << "Test" << i << endl;
compiles fine in VS2013, but fails in GCC with unreadable message. It seems that the behaviour of VS2013 is correct.
vector
gets big enough, contents should be moved to a new memory area.Though I couldn't find a right place in standard that says something articulate on this. Could someone quote it, please?
If you scroll down to the end of the errors thrown by clang you'll see this one:
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/basic_ios.h:66:23: note: copy constructor of 'basic_ios<char, std::char_traits<char> >' is implicitly deleted because base class 'std::ios_base' has an inaccessible copy constructor
This is the corresponding line from gcc's long list of errors:
/usr/include/c++/4.8/bits/basic_ios.h:66:11: note: 'std::basic_ios<char>::basic_ios(const std::basic_ios<char>&)' is implicitly deleted because the default definition would be ill-formed:
class basic_ios : public ios_base
This is because libstdc++ is missing move constructors for basic_ios
, as listed here on the status page.
27.5 | Iostreams base classes | Partial | Missing move and swap operations on
basic_ios
.
And here's the associated bugzilla. Your code compiles with clang if you use libc++.
A simpler example, copied from the bug report, also fails to compile:
#include <sstream>
#include <utility>
std::stringstream getss(){
std::stringstream q;
return std::move(q);
}
To fix your problem:
template<typename T, typename...Args>
std::unique_ptr<T> make_unique(Args&&...args) {
return std::unique_ptr<T>( new T(std::forward<Args>(args)...) );
}
and if you hate typing ofstream
:
template<typename...Args>
std::unique_ptr<std::ofstream> make_up_ofstream(Args&&...args) {
return make_unique<std::ofstream>(std::forward<Args>(args)...);
}
giving you:
std::vector<std::unique_ptr<std::ofstream>> v;
v.emplace_back(make_up_ofstream("file1.txt"));
v.emplace_back(make_up_ofstream("file2.txt"));
for (int i = 0, ilen = v.size(); i < ilen; ++i)
*(v[i]) << "Test" << i << endl;
which is pretty close, no?
This makes me want to write make_up
that deduces the type of the unique_ptr
you are assigning it to:
// dense boilerplate, obsolete in C++1y:
template<unsigned...>struct indexes{typedef indexes type;};
template<unsigned Max,unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
template<typename T>using type=T;
template<typename... Args>
struct up_maker {
std::tuple<Args...> args;
template<class T, class...Ds, unsigned...Is>
std::unique_ptr<T,Ds...> helper( indexes<Is...> ) && {
return std::unique_ptr<T,Ds...>( new T(std::forward<Args>( std::get<Is>(args) )...) );
}
template<class T, class...Ds>
operator type<std::unique_ptr<T,Ds...>>() && {
return std::move(*this).helper<T,Ds...>( make_indexes_t< sizeof...(Args) >{} );
}
explicit up_maker( Args&&... args_in ):args( std::forward<Args>(args_in)... ) {}
up_maker( up_maker const& ) = delete;
up_maker( up_maker && ) = default;
up_maker& operator=( up_maker const& ) = delete;
up_maker& operator=( up_maker && ) = default;
};
template<typename...Args>
up_maker<Args...> make_up( Args&&... args ) {
return up_maker<Args...>( std::forward<Args>(args)... );
}
which if I wrote it right, gets rid of more boilerplate in your code:
std::vector<std::unique_ptr<std::ofstream>> v;
v.emplace_back(make_up("file1.txt"));
v.emplace_back(make_up("file2.txt"));
for (int i = 0, ilen = v.size(); i < ilen; ++i)
(*v[i]) << "Test" << i << std::endl;
... lots of code just to get rid of two _ofstream
, but it was amusing.
live example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With