What is the meaning of ellipses in a copy constructor? - c++

Consider the following program:
#include <iostream>
struct Test
{
int a;
Test() : a(3)
{ }
Test(const Test& t...)
{
std::cout<<"Copy constructor called\n";
a=t.a;
}
int get_a()
{
return a;
}
~Test()
{
std::cout<<"Destructor is called\n";
}
};
int main()
{
Test t;
Test* t1=new Test(t);
std::cout<<t.get_a()<<'\n';
std::cout<<t1->get_a()<<'\n';
delete t1;
}
Closely observe the three dots in parameter of copy constructor
I was really surprised when I tried this program. What is the use of it? What does it mean?
What the language specification says about this?
I know that three dots are used to represent variable length arguments in variadic functions
like printf() and scanf() etc and also variadic macros introduced by C99. In C++, if I am not wrong, they are used in variadic templates.
Is this code well formed? Is this variadic copy constructor that can take any number of arguments?
It compiles & runs fine on g++ 4.8.1 & MSVS 2010.

The draft standard in section 8.3.5 [dcl.fct] says , ... is synonymous with ... unless ... is part of abstract-declarator (emphasis mine):
[...]If the parameter-declaration-clause terminates with an ellipsis
or a function parameter pack (14.5.3), the number of arguments shall
be equal to or greater than the number of parameters that do not have
a default argument and are not function parameter packs. Where
syntactically correct and where “...” is not part of an
abstract-declarator, “, ...” is synonymous with “...”.[...]
So it is a variadic function and as far as I can tell without additional arguments this is also a valid copy constructor, from section 12.8 [class.copy]:
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 (8.3.6).
and this note says that ellipses are not parameters:
void g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow
// a parameter with a default argument
which is backed up by the normative text above which says:
If the parameter-declaration-clause terminates with an ellipsis[...]
Note, since it was asked an abstract-declarator is a declarator without an identifier.

What is the use of it? What does it mean?
Yes, it introduces a variadic function.
In C++ If I am not wrong they are used in variadic templates.
The syntax and semantics are different. It's a "C-style" variadic function, not a variadic template function. A copy constructor, moreover, cannot be a template function.
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 (8.3.6).
Per §12.8.2 in the final draft (emphasis mine)
Is this code well formed? Is this variadic copy constructor that can take any number of arguments?
If the ellipsis contains parameters, it is no more a copy constructor but a simple constructor. If there aren't, then it is a valid copy constructor.
X(const X&, int = 1, double = 5); // copy-ctor
X(const X&, int = 1, double); // constructor

Related

C++11 uniform initialization: ambiguity between initializer list and multiple-parameter constructors?

Currently trying to wrap my head around C++11's uniform initialization. I came upon this ambiguous case: consider a class which can either be constructed from either a two-argument constructor or an initializer list of any length:
class Foo {
public:
Foo(int a, int b) {
std::cout << "constructor 1" << std::endl;
}
Foo(std::initializer_list<int>) {
std::cout << "constructor 2" << std::endl;
}
};
Following uniform initialization convention, I'd expect the following to work:
Foo a (1, 2) prints constructor 1 (duh)
Foo b {1, 2} prints constructor 1
Foo c = {1, 2} prints constructor 2
However, it seems like the compiler interprets Foo b {1, 2} as a list initialization, and calls constructor 2. Is the () syntax the only way to force the compiler to consider other kinds of constructors when an initializer-list constructor is present?
it seems like the compiler interprets Foo b {1, 2} as a list
initialization, and calls constructor 2. Is the () syntax the only way
to force the compiler to consider other kinds of constructors when an
initializer-list constructor is present?
Quotes from standard draft explains this well:
9.4.5.2 [dcl.init.list] (emphasis mine):
A constructor is an initializer-list constructor if its first
parameter is of type std​::​initializer_­list or reference to cv
std​::​initializer_­list for some type E, and either there are no
other parameters or else all other parameters have default arguments
([dcl.fct.default]).
[Note 2: Initializer-list constructors are
favored over other constructors in list-initialization
([over.match.list]). Passing an initializer list as the argument to
the constructor template template C(T) of a class C does not
create an initializer-list constructor, because an initializer list
argument causes the corresponding parameter to be a non-deduced
context ([temp.deduct.call]). — end note]
and 12.4.2.8 [over.match.list]:
When objects of non-aggregate class type T are list-initialized such
that [dcl.init.list] specifies that overload resolution is performed
according to the rules in this subclause or when forming a
list-initialization sequence according to [over.ics.list], overload
resolution selects the constructor in two phases:
If the initializer list is not empty or T has no default constructor,
overload resolution is first performed where the candidate functions
are the initializer-list constructors ([dcl.init.list]) of the class T
and the argument list consists of the initializer list as a single
argument.
Otherwise, or if no viable initializer-list constructor is found,
overload resolution is performed again, where the candidate functions
are all the constructors of the class T and the argument list consists
of the elements of the initializer list.
You can add an extra ignored argument to your constructor to specify a particular overload at callsite, like they do in STL:
#include <iostream>
struct non_init_list_t {};
inline constexpr non_init_list_t non_init_list;
struct Class {
Class(int a, int b, non_init_list_t = non_init_list) { std::clog << "()\n"; }
Class(std::initializer_list<int> list) { std::clog << "{}\n"; }
};
Class a{12, 42, non_init_list}; // ()
Class b{12, 42}; // {}
Class c(12, 42); // ()
If the constructor has the initializer_list version, the compiler will first interpret it as initializer_list, if there is no initializer_list version, the compiler will interpret it as another overloaded version.
If the compiler interprets it as another version, and you want to call a constructor that uses the initializer_list version, it happens that the number and type of arguments are the same as other ctors, then your will cause a bug. Then the compiler chooses the initializer_list version or other version? So using bracket notation is definitely not the initializer_list version. If you don't have an initializer_list version in your constructor, don't worry about this issue.
BTW, if you use auto to infer type automatically, DO NOT use uniform initialization. It must interpret type to initializer_list.

noexcept specifier with default arguments construction

Take the following example code:
void test(const Item& item = Item()) {
...
}
Assume that, once item has been passed to the function, this cannot throw.
The question is: the function should be marked noexcept or noexcept(noexcept(Item()))?
IHMO, the former should be ok, but I am not sure. A quotation from the standard would be very appreciated!
Default arguments are shortcut notations for the caller of function. So, when the function executes, the construction is already complete.
Thus, noexcept should be sufficient.
In the standard [dcl.fct.default] states:
If an initializer-clause is specified in a parameter-declaration this
initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
Example:
the declaration
void point(int = 3, int = 4);
declares a function that can be called with zero, one, or two arguments of type int. It can be called in
any of these ways:
point(1,2); point(1); point();
The last two calls are equivalent to
point(1,4) and point(3,4) , respectively.
Also there is a note (in [intro.execution] Program execution):
Subexpressions involved in evaluating default arguments (8.3.6) are
considered to be created in the expression that calls the function, not the expression that defines the default
argument

Copy constructor's signature without reference

If I have a Point class, the copy constructor should look like this:
Point(const Point &p);
Point(Point &p);
However, what if I want to create a constructor, which requires a Point? Why is it considered as copy constructor, instead of a constructor?
Point(const Point p)
Compiler error: "copy constructor for class "Point" may not have a parameter of type "Point"
You can not have a copy constructor signature to accept the argument by value. The reason is simple - in order to pass parameter by value, you need to invoke copy constructor, which will require to pass parameter by value, and will invoke copy constructor... Welcome to endless recursion.
Compiler is saving you a lot of trouble by not allowing this construct.
Point(const Point p)
Why is it considered as copy constructor, instead of a constructor?
It's not.
As the standard says in §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 (...).
In fact, your declaration is ill-formed. §12.8/6 says:
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.
You do have exactly that: a constructor for a class Point whose first parameter is of type const Point and there are no other arguments.
This is of course the formal explanation. As others have explained, the practical implication of such a constructor would be infinite recursion.
Perhaps you are concerned about the error message you got. However, there are absolutely no rules regarding the contents of a diagnostic message produced by a compiler. It's a quality-of-implementation issue; if your compiler thinks that copy constructor for class "Point" may not have a parameter of type "Point" is a good way to convey the problem to its users, then so be it.

Template "copy constructor" does not prevent compiler-generated move constructor

Consider the following program and the comments in it:
template<class T>
struct S_ {
S_() = default;
// The template version does not forbid the compiler
// to generate the move constructor implicitly
template<class U> S_(const S_<U>&) = delete;
// If I make the "real" copy constructor
// user-defined (by deleting it), then the move
// constructor is NOT implicitly generated
// S_(const S_&) = delete;
};
using S = S_<int>;
int main() {
S s;
S x{static_cast<S&&>(s)};
}
The question is: why does not user-defining the template constructor (which effectively acts as a copy constructor when U = T) prevent the compiler from generating the move constructor, while, on the contrary, if I user define the "real" copy constructor (by deleting it), then the move constructor is not generated implicitly (the program would not compile)? (Probably the reason is that "template version" does not respect the standard definition of copy-constructor also when T = U?).
The good thing is that that seems to apparently be what I want. In facts, I need all the copy and move constructors and all the move and copy assignment operators that the compiler would generate implicitly as if S was simply defined as template<class U> S{}; plus the template constructor for the conversions from other S<U>. By standard, can I rely on the above definition of S to have all the mentioned stuff I need? If yes, I could then avoid to "default'ing" them explicitly.
The answer is simple - There is no (!) template copy constructor. Even if the template parameter matches the parameter of a copy constructor it is no copy constructor.
See 12.8 Copying and moving class objects
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 (8.3.6). [ Example: X::X(const X&)
and X::X(X&,int=1) are copy constructors.
Similar applies to the move constructor
There is no such thing as a templated copy constructor. The regular (non-templated) copy constructor is still generated and a better match than the templated one with the signature you provided (things get more complicated with "universal" references thrown in the mix, but that is off-topic).
Copy constructors, and move constructors, are not templated. The C++ standard is specific on what constitutes a copy constructor.
From: http://en.cppreference.com/w/cpp/language/copy_constructor
A copy constructor of class T is a non-template constructor whose first parameter is T&, const T&, volatile T&, or const volatile T&, and either there are no other parameters, or the rest of the parameters all have default values.

Copy constructor with 2 argument

class Sample
{
public:
Sample(){}
Sample(const Sample& obj){ cout<<"C.C. with 1 argument called"<<endl;}
Sample(const Sample& obj, int i){ cout<<"C.C. with 2 arguments called"<<endl;}
};
void main()
{
Sample s1;
Sample s2 = s1; // Here, C.C with 1 arg. called.
}
There are few questions:
How I can make a call to copy constructor having 2 arguments?
When we require a copy constructor with 1 argument and when we require C.C with 2 argument?
A constructor with 2 (or more) required arguments is not a copy constructor.
1.:
Sample s2(s1, 0);
Just to add little formalism here. Standard has strict definition for "Copy constructor" term (12.8 ):
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 (8.3.6). [ Example: X::X(const X&) and X::X(X&,int=1) are copy constructors.
A class will only really have one copy ctor, which can be invoked with only one argument. It can take two (or more) arguments, but only if it provides default values for the other arguments. Either way, the first argument must be a (normally const) reference to an object of the same type.
Your second ctor taking two arguments isn't really a copy ctor (at least as the term is normally used) -- it's just a ctor that happens to take an instance as an argument (may base the new instance on that argument, at least in part).