I got confused today, while implementing a forwarding constructor. Here's the code snippet:
# include <iostream>
# include <iterator>
# include <vector>
struct Vec
{
explicit Vec() = default;
Vec(Vec&&) = default;
Vec(const Vec&) = default;
Vec& operator=(Vec&&) = default;
Vec& operator=(const Vec&) = default;
template<typename ...Args> Vec(Args &&...args): // Yes, this is dangerous. SFINAE needed.
_vec(std::forward<Args>(args)...) {}
const std::vector<int> & get() const { return _vec; }
std::vector<int> _vec;
};
As suggested by many of the greatest, the line with the comment is dangerous since it contains overloading (variadic templates) forwarding / universal references, which will probably "monopoly" the overload resolution (against default or implicit generated copy / move constructor). Extra SFINAE constraints on variadic templates are needed to make this work properly.
So as expected, the following code does not compile:
Vec vec(5, 10);
Vec vec_copy(vec); // This line does not compile.
The second line with the copy construction resolves to the forwarding constructor: no surprises here.
But since I was experimenting, I wrote the following wrapper class (just for fun):
struct Wrapper
{
explicit Wrapper() = default;
Wrapper(const Vec &vec): _data(vec) {};
Wrapper(Wrapper&&) = default;
Wrapper(const Wrapper &rhs)
{
Vec vec(rhs._data); // I wrote it like this on purpose.
_data = vec;
};
Wrapper& operator=(Wrapper&&) = default;
Wrapper& operator=(const Wrapper&) = default;
Vec _data;
};
And now the following code snippet compiles and runs as (un)expectly (with g++-5.3 -std=c++14 -O3 -Wall):
Vec vec(5, 10);
// Console output.
std::copy(vec.get().begin(), vec.get().end(),
std::ostream_iterator<int>(std::cout, " "));
Wrapper wrap(vec);
Wrapper warp_copy = wrap; // This copy construction does the "right" thing.
// Verify copy.
std::cout << '\n';
std::copy(vec.get().begin(), vec.get().end(),
std::ostream_iterator<int>(std::cout, " "));
And it outputs:
$ ./fwd.exe
10 10 10 10 10
10 10 10 10 10
My question is:
- Why suddenly the copy construction of
Vecobjects insideWrapperclass get resolved correctly? - I wrote the line
Vec vec(rhs._data);on purpose. If I'm understanding correctly here,rhs._datareturns an lvalue reference toVec _data, so this line is essentially the same as the previousVec vec_copy(vec);, which does not compile outside of theWrapperclass, right? - Funny thing, if I do this:
Vec vec_dup(wrap._data);in the main, outside theWrapperclass, it does not compile. The same error is reported, saying that it got resolved into the forwarding constructor.
Am I missing something here? Is this the expected behavior? It has been really bugging for a while ...
Aucun commentaire:
Enregistrer un commentaire