In an answer to Is it safe to store objects of a class which has an std::auto_ptr as its member variable in std::vector? I stated that a class that contained an auto_ptr could be stored in a vector provided the class had a user-defined copy constructor.
There were several comment suggesting that this was not the case, so this question is an attempt to clear the issue up. Consider the following code:
#include <memory>
#include <vector>
using namespace std;
struct Z {};
struct A {
A( Z z )
: p( new Z(z) ) {}
A( const A & a )
: p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}
// no assigment op or dtor defined by intent
auto_ptr <Z> p;
};
int main() {
vector <A> av;
Z z;
A a(z);
av.push_back( a );
av.push_back( A(z) );
av.clear();
}
Please examine the above & in your reply indicate where undefined
behaviour in the meaning of the C++ Standard could occur for this particular class used in this particular way. I am not interested whether the class is useful, well-behaved, sortable, or how it performs under exceptions.
Please also note that this is not a question about the validity of creating a vector of auto_ptrs - I am well aware of the issues regarding that.
Thanks all for your inputs on what in
retrospect is probably a rather silly
question. I guess I focussed too much
on the copy ctor & forgot about
assignment. The lucky winner of my
acceptance points (and points mean
prizes!) is litb for a typically
exhaustive explanation (sorry
earwicker)
Objects stored in containers are required to be "CopyConstructable" as well as "Assignable" (C++2008 23.1/3).
Your class tries to deal with the CopyConstructable requirement (though I'd argue it still doesn't meet it - I edited that argument out since it's not required and because it's arguable I suppose), but it doesn't deal with the Assignable requirement. To be Assignable (C++2008 23.1/4), the following must be true where t is a value of T and u is a value of (possibly const) T:
t = u returns a T& and t is equivalent to u
The standard also says in a note (20.4.5/3): "auto_ptr does not meet the CopyConstructible and Assignable requirements for Standard Library container elements and thus instantiating a Standard Library container with an auto_ptr results in undefined behavior."
Since you don't declare or define an assignment operator, an implicit one will be provided that uses the auto_ptr's assignment operator, which definitely makes t not equivalent to u, not to mention that it won't work at all for "const T u" values (which is what Earwicker's answer points out - I'm just pointing out the exact portion(s) of the standard).
Trying to put the list of places together that makes the example undefined behavior.
#include <memory>
#include <vector>
using namespace std;
struct Z {};
struct A {
A( Z z )
: p( new Z(z) ) {}
A( const A & a )
: p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}
// no assigment op or dtor defined by intent
auto_ptr <Z> p;
};
int main() {
vector <A> av;
...
}
I will examine the lines up to the one where you instantiate the vector with your type A. The Standard has to say
In 23.1/3:
The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types.
In 23.1/4 (emphasis mine):
In Table 64, T is the type used to instantiate the container, t is a value of T, and u is a value of (possibly const) T.
+-----------+---------------+---------------------+
|expression |return type |postcondition |
+-----------+---------------+---------------------+
|t = u |T& |t is equivalent to u |
+-----------+---------------+---------------------+
Table 64
In 12.8/10:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
each direct base class B of X has a copy assignment operator whose parameter is of type const B&,
const volatile B& or B, and
for all the nonstatic data members of X that are of a class type M (or array thereof), each such class type has a copy assignment operator whose parameter is of type const M&, const volatile M& or M.
Otherwise, the implicitly declared copy assignment operator will have the form
X& X::operator=(X&)
(Note the last and second last sentence)
In 17.4.3.6/1 and /2:
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ Standard Library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
In particular, the effects are undefined in the following cases:
for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (20.1.5, 23.1, 24.1, 26.1). Operations on such types can report a failure by throwing an exception unless otherwise specified.
Now, if you look at the specification of auto_ptr you will note it has a copy-assignment operator that takes a non-const auto_ptr. Thus, the implicitly declared copy assignment operator of your class will also take a non-const type as its parameter. If you read the above places carefully, you will see how it says that instantiating a vector with your type as written is undefined behavior.
I don't think it's necessarily the case that the above code will even compile. Surely the implementor of std::vector is at liberty to require an assignment operator to be available, from const A&?
And having just tried it, it doesn't compile on Visual Studio C++ 2008 Service Pack 1:
binary '=' : no operator found which
takes a right-hand operand of type
'const A' (or there is no acceptable
conversion)
My guess is that, on the guidance of Herb Sutter, the container classes in VC++ make every effort to impose the standard requirements on their type parameters, specifically to make it hard to use auto_ptr with them. They may have overstepped the boundaries set by the standard of course, but I seem to remember it mandating true assignment as well as true copy construction.
It does compile in g++ 3.4.5, however.
Since the regular auto_ptr semantic could suggests that the ownership is passed during the copying, I would rather use here boost::scoped_ptr. Of course the assignment operator is missing.
What about the following?
cout << av[ 0 ] << endl;
Also, conceptually, a copy should leave the item copied from unchanged. This is being violated in your implementation.
(It is quite another thing that your original code compiles fine with g++ -pedantic ... and Comeau but not VS2005.)
Related
The builtin operator-> is defined as (*p).m, which is just fine for my iterator, so overloading it would just waste my time and the maintainer's eyes.
Just trying it wouldn't guarantee portability, and I haven't been able to find an answer, though I fear that it is no, because apparently nobody has even considered it before.
Update:
I made a minimal test program to actually try this:
struct S { int m;};
struct P
{ auto& operator*() const { return s;}
auto operator->() const =default;// { return &s;}
S s;
};
int main()
{ P p;
p->m;
}
g++ (Debian 8.3.0-6) compiles this only without =default;//, so seems like defaulting or omitting the overload won't be portable for years at least.
Will built-in operator-> be used if I don't overload it?
No, only certain special member functions are implicitly declared for a given class-type(and that too under certain circumstances). And operator-> is not one of them. This can be seen from special members which states:
The six special members functions described above are members implicitly declared on classes under certain circumstances:
Default ctor
Dtor
Copy ctor
Copy assignment
Move ctor
Move assignment
(emphasis mine)
Note in the above list, there is no mention of operator->. This means that if you want to use -> with an object of your class-type then you must overload it explicitly.
Now, coming to your question about the error that you're getting.
compiles this only with the commented out definition, so seems like defaulting or omitting the overload won't be portable for years at least.
You're getting the error because operator-> cannot be defaulted. This can be seen from the same special members documentation which says:
each class can select explicitly which of these members exist with their default definition or which are deleted by using the keywords default and delete, respectively.
(emphasis mine)
Note the emphasis on "these members" above. In particular, only the six special members listed above can be defaulted. And again since operator-> is not one of them, it can't be defaulted using default.
As Anoop Rana pointed out, only special member functions can be defaulted, and, as Yksisarvinen said, the builtin operator-> exists only for builtin types.
Redundancy in overloaded operators is a long acknowledged problem.
Boost::Operators provides common overloads with CRTP,
including operator-> that mimics the builtin behavior:
#include <boost/operators.hpp>
struct S { int m;};
struct P : boost::dereferenceable< P, const S*>
{ auto& operator*() const { return s;}
S s;
};
int main()
{ P p;
p->m;
}
Unfortunately spelling out the return type is required.
(It shouldn't be required IMHO.)
Alone it isn't a big step forward, but
it's bundled in commonly needed groups like input_iteratable.
Link https://cplusplus.github.io/LWG/issue2067 provides the following discussion:
Class template packaged_task is a move-only type with the following form of the deleted copy operations:
packaged_task(packaged_task&) = delete;
packaged_task& operator=(packaged_task&) = delete;
Note that the argument types are non-const. This does not look like a typo to me, this form seems to exist from the very first proposing paper on N2276. Using either of form of the copy-constructor did not make much difference before the introduction of defaulted special member functions, but it makes now an observable difference. This was brought to my attention by a question on a German C++ newsgroup where the question was raised why the following code does not compile on a recent gcc:
#include <utility>
#include <future>
#include <iostream>
#include <thread>
int main() {
std::packaged_task<void()> someTask([]{ std::cout << std::this_thread::get_id() << std::endl; });
std::thread someThread(std::move(someTask)); // Error here
// Remainder omitted
}
It turned out that the error was produced by the instantiation of some return type of std::bind which used a defaulted copy-constructor, which leads to a const declaration conflict with [class.copy] p8.
Some aspects of this problem are possibly core-language related, but I consider it more than a service to programmers, if the library would declare the usual form of the copy operations (i.e. those with const first parameter type) as deleted for packaged_task to prevent such problems.
Could anybody explain the meaning of the marked statement? I don't undestand how the missing const qualifer affects the compilation process, and how this behavior is explained in standard.
What is the point of adding const to the parameter of the deleted copy constructor?
Here is a toy example:
struct problem {
problem()=default;
problem(problem&&)=default;
problem(problem&)=delete;
};
template<class T>
struct bob {
T t;
bob()=default;
bob(bob&&)=default;
bob(bob const&)=default;
};
int main() {
problem p;
problem p2 = std::move(p);
bob<problem> b;
bob<problem> b2 = std::move(b);
}
bob<problem> fails to compile because the bob(bob const&)=default errors out when it interacts with problem(problem&)=delete.
Arguably the standard "should" error-out cleanly when it determines that it cannot implement bob(bob const&), and treat the =default as =delete (like it would if we had problem(problem const&)=delete), but the standard wording isn't going to be flawless in this corner case of a corner case. And this corner of a corner case is going to be strange and quirky enough that I'm not certain a general rule that makes it translate =default to =delete would be right!
The fix if we problem(problem const&)=delete (well, to packaged_task) is going to be so much cleaner than anything we do to =default ctor rules.
Now standard delving:
First, it is obvious that the implicitly declared copy constructor of bob<problem> above is going to have signature bob(bob&) in [class.ctor]. I won't even quote the standard for that, because lazy.
We go and explicitly default bob(bob const&) copy ctor, which differs in signature from the one that would be implicitly declared.
There are rules about explicitly defaulting functions and their conflict with the signatures is in 11.4.2.
In Explicitly-defaulted functions[dcl.fct.def.default] 11.4.2/2
2 The typeT1of an explicitly defaulted function F is allowed to differ from the type T2 it would have had if it were implicitly declared, as follows:
—(2.1) T1 and T2 may have differing ref-qualifiers; and
—(2.2) if T2 has a parameter of type const C&, the corresponding parameter of T1 may be of type C&.
If T1 differs from T2 in any other way, then:
—(2.3) if F is an assignment operator, and the return type of T1 differs from the return type of T2 or T1’s parameter type is not a reference, the program is ill-formed;
—(2.4) otherwise, if F is explicitly defaulted on its first declaration, it is defined as deleted;
—(2.5) otherwise, the program is ill-formed.
The defaulted one is T1, which contains const& not &, so (2.2) doesn't apply.
My reading actually has it getting caught on (2.4); the type of bob(bob const&) differs from the implicitly declared bob(bob&) in an impermissible way; but first declaration is defaulted, so it should be deleted.
I'm looking at the n4713 draft version; maybe an older version didn't have that clause.
Given:
struct X {
int m;
std::string s;
};
I can do:
X x; // invokes automatically defined default ctor
X y = { 5 }; // invokes whatever became of the original struct initialization but now maybe runs through C++ initializer-lists?
X z = { 5, "yolo" }; // I assume this is an initializer-list that is being handled by some rule for structs that either runs through a compiler created ctor or copy-from-initializer-list that is similarly compiler-created
and even...
std::vector<X> vx;
vx.push_back({ 99, "yo" }); // okay
But not...
vx.emplace_back(99, "yo"); // error VS 2017 v. 15.7.4
vx.emplace_back({99, "yo"}); // error VS 2017 v. 15.7.4
I'm not understanding the rules between initializer-lists, implicitly defined (or compiler defined) ctors, and forwarding functions like emplace_back()
Would someone be so kind as to either point me to the necessary bits of the standard or a good article on an in-depth discussion of what's become of all of the rules around structs and implicit construction and other compiler-supplied members such as copy / move operators?
I seem to be in need of a more comprehensive rules lesson - because it seems like emplace_back() ought to work for one of either emplace_back(int, std::string), or for emplace_back(initializer-list) - no?
X is an aggregate. While the specific definition of aggregate has changed in every standard, your type is an aggregate in all of them.
List-initialization for an aggregate does aggregate-initialization here. There's no constructor here - there's no "auto" constructor, no synthesized constructor. Aggregate-initialization does not create constructors or go through that mechanism. We're directly initializing each class member from the appropriate initializer in the braced-init-list. That's what both your y and your z are doing.
Now, for the second part. The relevant part of vector looks like:
template <typename T>
struct vector {
void push_back(T&&);
template <typename... Args>
void emplace_back(Args&&...);
};
A braced-init-list, like {99, "yo"}, does not have a type. And you cannot deduce a type for it. They can only be used in specific circumstances. push_back({99, "yo"}) works fine because push_back takes an X&& - it's not a function template - and we know how to do that initialization.
But emplace_back() is a function template - it needs to deduce Args... from the types of its arguments. But we don't have a type, there's nothing to deduce! There are some exceptions here (notably std::initializer_list<T> can be deduced), but here, we're stuck. You would have to write emplace_back(X{99, "yo"}) - which creates the X on the caller's side.
Similarly, emplace_back(99, "yo") doesn't work because emplace uses ()s to initialize, but you cannot ()-initialize an aggregate. It doesn't have a constructor!
This code
#include <iostream>
#include <optional>
struct foo
{
explicit operator std::optional<int>() {
return std::optional<int>( 1 );
}
explicit operator int() {
return 2;
}
};
int main()
{
foo my_foo;
std::optional<int> my_opt( my_foo );
std::cout << "constructor: " << my_opt.value() << std::endl;
my_opt = static_cast<std::optional<int>>(my_foo);
std::cout << "static_cast: " << my_opt.value() << std::endl;
}
produces the following output
constructor: 2
static_cast: 2
in Clang 4.0.0 and in MSVC 2017 (15.3). (Let's ignore GCC for now, since it's behavior seems to be buggy in that case.)
Why is the output 2? I would expect 1. The constructors of std::optional seem to prefer casting to the inner type (int) despite the fact that a cast to the outer type (std::optional<int>) is available. Is this correct according to the C++ standard? If so, is there a reason the standard does not dictate to prefer an attempt to cast to the outer type? I would find this more reasonable and could imagine it to be implemented using enable_if and is_convertible to disable the ctor if a conversion to the outer type is possible. Otherwise every cast operator to std::optional<T> in a user class - even though it is a perfect match - would be ignored on principle if there is also one to T. I would find this quite obnoxious.
I posted a somewhat similar question yesterday but probably did not state my problem accurately, since the resulting discussion was more about the GCC bug. That's why I am asking again more explicitly here.
In case Barry's excellent answer still isn't clear, here's my version, hope it helps.
The biggest question is why isn't the user-defined conversion to optional<int> preferred in direct initialization:
std::optional<int> my_opt(my_foo);
After all, there is a constructor optional<int>(optional<int>&&) and a user-defined conversion of my_foo to optional<int>.
The reason is the template<typename U> optional(U&&) constructor template, which is supposed to activate when T (int) is constructible from U and U is neither std::in_place_t nor optional<T>, and direct-initialize T from it. And so it does, stamping out optional(foo&).
The final generated optional<int> looks something like:
class optional<int> {
. . .
int value_;
. . .
optional(optional&& rhs);
optional(foo& rhs) : value_(rhs) {}
. . .
optional(optional&&) requires a user-defined conversion whereas optional(foo&) is an exact match for my_foo. So it wins, and direct-initializes int from my_foo. Only at this point is operator int() selected as a better match to initialize an int. The result thus becomes 2.
2) In case of my_opt = static_cast<std::optional<int>>(my_foo), although it sounds like "initialize my_opt as-if it was std::optional<int>", it actually means "create a temporary std::optional<int> from my_foo and move-assign from that" as described in [expr.static.cast]/4:
If T is a reference type, the effect is the same as performing the
declaration and initializationT t(e); for some invented temporary
variable t ([dcl.init]) and then using the temporary variable as the
result of the conversion. Otherwise, the result object is
direct-initialized from e.
So it becomes:
my_opt = std::optional<int>(my_foo);
And we're back to the previous situation; my_opt is subsequently initialized from a temporary optional, already holding a 2.
The issue of overloading on forwarding references is well-known. Scott Myers in his book Effective Modern C++ in Chapter 26 talks extensively about why it is a bad idea to overload on "universal references". Such templates will tirelessly stamp out whatever the type you throw at them, which will overshadow everything and anything that is not an exact match. So I'm surprised the committee chose this route.
As to the reason why it is like this, in the proposal N3793 and in the standard until Nov 15, 2016 it was indeed
optional(const T& v);
optional(T&& v);
But then as part of LWG defect 2451 it got changed to
template <class U = T> optional(U&& v);
With the following rationale:
Code such as the following is currently ill-formed (thanks to STL for
the compelling example):
optional<string> opt_str = "meow";
This is because it would require two user-defined conversions (from
const char* to string, and from string to optional<string>) where the
language permits only one. This is likely to be a surprise and an
inconvenience for users.
optional<T> should be implicitly convertible from any U that is
implicitly convertible to T. This can be implemented as a non-explicit
constructor template optional(U&&), which is enabled via SFINAE only
if is_convertible_v<U, T> and is_constructible_v<T, U>, plus any
additional conditions needed to avoid ambiguity with other
constructors...
In the end I think it's OK that T is ranked higher than optional<T>, after all it's a rather unusual choice between something that may have a value and the value.
Performance-wise it is also beneficial to initialize from T rather than from another optional<T>. An optional is typically implemented as:
template<typename T>
struct optional {
union
{
char dummy;
T value;
};
bool has_value;
};
So initializing it from optional<T>& would look something like
optional<T>::optional(const optional<T>& rhs) {
has_value = rhs.has_value;
if (has_value) {
value = rhs.value;
}
}
Whereas initializing from T& would require less steps:
optional<T>::optional(const T& t) {
value = t;
has_value = true;
}
A static_cast is valid if there is an implicit conversion sequence from the expression to the desired type, and the resulting object is direct-initialized from the expression. So writing:
my_opt = static_cast<std::optional<int>>(my_foo);
Follows the same steps as doing:
std::optional<int> __tmp(my_foo); // direct-initialize the resulting
// object from the expression
my_opt = std::move(__tmp); // the result of the cast is a prvalue, so move
And once we get to construction, we follow the same steps as my previous answer, enumerating the constructors, which ends up selecting the constructor template, which uses operator int().
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
What are copy constructors?
Why do we use them? Is it necessary to call a destructor if we do?
What is a copy constructor?
Quoting the C++11 standard, §12.8/2:
“A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&,
volatile X& or const volatile X&, and either there are no other parameters or else all other parameters
have default arguments”
So there are 4 different forms. Of these the X const& form is the one most often used. This is also the form of the implicit copy constructor that is generated (if possible) if no copy constructor is declared for the class.
A copy constructor is used for copying an object to a new instance of the class. A default copy constructor performs a member-wise copy, which is different from and more safe than just copying the bits. In particular a copy constructor or move constructor is logically used for a copy initialization, an initialization using the = sign like
Some_type x = expression;
In some circumstances the compiler is allowed to assume that copying is all that a copy constructor does, regardless of what it actually does. Logically needless copy constructions can then be removed, or as the standard calls it, elided. For example, if the expression above is the number 42, then instead of using that argument to construct a temporary and copying or moving that to x, the argument may be used to initialize x directly.
However, even when that is done, so that the copy constructor is not used, if the copy constructor would have been used except for the optimization then a copy constructor must exist and be accessible.
C++11 §12.2/1:
“Even when the creation of the temporary object is unevaluated (Clause 5)
or otherwise avoided (12.8), all the semantic restrictions shall be respected as if the temporary object had
been created and later destroyed.”
Is it necessary to call a destructor?
Re “Is it necessary to call a destructor if we [use a copy constructor]? ”.
Generally no.
Destructors in C++ are called automatically when an object goes out of scope or when it's indirectly or directly destroyed via delete. The only exception is for placement new, which is a low level feature that has nothing in particular to do with copy constructors. Although placement new can involve copy construction.
A difference between C++03 and C++11 copy constructors.
To understand the difference between C++03 and C++11 regarding copy constructors you must be aware of three facts:
If a copy constructor isn’t declared, then it’s generated if possible. Hence by default every class has a copy constructor. It’s there.
Access is independent of overload resolution.
Overload resolution chooses between all member functions, regardless of whether they’re accessible or not. Then access is checked. At that point you may get an error message about calling an inaccessible function, despite an expectation that overload resolution ideally should choose only from the accessible functions…
An ordinary function (or just function) is a better match than a an instantiation of a function template with the same signature.
Here is an example of overloading resolution choosing a private member function, in spite of a public function that could be called:
File [access_versus_overload_resolution.cpp]
class C
{
public:
void foo( double );
void foo( int );
void bar( double );
private:
void bar( int );
};
int main()
{
C().foo( 42 ); // OK.
C().bar( 42 ); //! Uh oh, inaccessible.
}
And here is an example of an ordinary function trumping a template instantiation:
File [function_versus_template.cpp]
using One = char[1];
using Two = char[2];
auto foo( char const* ) -> One&;
auto foo( char const (&)[10] ) -> Two&;
auto bar( char const* ) -> One&;
template< int n >
auto bar( char const (&)[n] ) -> Two&;
#include <iostream>
auto main() -> int
{
using namespace std;
//cout << sizeof( foo( "Blah blah" ) ) << endl; //! Ambiguous
cout << sizeof( bar( "Blah blah" ) ) << endl; // "1"
}
With these facts at hand:
An inaccessible copy constructor will be chosen even if there is an accessible member function template that is just as good a match.
(As a logical consequence of the general overload resolution rules.)
In C++11, a member function template instantiation will therefore be chosen only if it is a better match than the copy constructor, if any.
But in C++03 there was a special rule that ensured that with a conforming compiler a function member template would never be used to copy an object.
Here’s the C++03 wording, §12.8/3 in that standard, where I’ve added suitable emphasis:
“A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-
qualified) X and either there are no other parameters or else all other parameters have default arguments. A
member function template is never instantiated to perform the copy of a class object to an object of its class
type.
while in C++11 the wording is subtly changed, in that standard’s §12.8/6, where again I’ve added suitable emphasis:
“A declaration of a constructor for a class Xis ill-formed if its first parameter is of type (optionally cv-qualified)
X and either there are no other parameters or else all other parameters have default arguments. A member
function template is never instantiated to produce such a constructor signature.”
I.e. no special injunction against function templates being used to copy objects in C++11.
Unfortunately for history buffs, the most readily available C++03 compiler for Windows, namely MinGW g++ of some old version such as 3.2, does not enforce the C++03 rule.
File [template_versus_cc.cpp]
class S
{
private:
S( S const& ); // No such.
public:
S() {}
template< class T >
S( T& ) {}
};
int main()
{
S sm;
S const sc;
S s1( sm ); // OK in C++11, template is better match.
S s2( sc ); //! Line 19. Copy constructor is selected, inaccessible.
}
Compiling with old g++:
[H:\dev\test\copy_construction]
> \bin\MinGW_3_1\bin\g++ --version | find "++"
g++ (GCC) 3.2.3 (mingw special 20030504-1)
[H:\dev\test\copy_construction]
> \bin\MinGW_3_1\bin\g++ -std=c++98 -pedantic template_versus_cc.cpp
template_versus_cc.cpp: In function `int main()':
template_versus_cc.cpp:4: `S::S(const S&)' is private
template_versus_cc.cpp:19: within this context
[H:\dev\test\copy_construction]
> _
By the C++03/C++98 rules the compiler should have flagged both copy initializations, at lines 18 and 19, but only flagged the latter, behaving as a C++11 compiler in this respect.
This is also what the more modern g++ 4.8.2 does, as well as Visual C++ 12.0. But a conforming C++03-compiler must diagnose both initializations.
Scott Meyers has written about this, citing two of the SO C++ Lounge dwellers as sources. Unfortunately the article yields the impressions that (1) it’s all about universal reference template arguments in C++11, and (2) there’s is no bug in g++. The former is wrong, as shown above, while the latter is literally correct. There was a bug, but as the standard has evolved, with C++11 the g++ behavior is now conforming…
Copy constructors are important for quite the same reasons as a regular constructor would be, and that is to properly initialize the constructed object. Construction is not always trivial, and it doesn't matter where do you take the data from and in what form - other object of the same class or a set of simpler arguments.
Therefore copy constructor is there to solve problems like initializing const fields, but also when you use pointers, to decide on ownership of the pointed resource. You may want to share, or duplicate the data.