could someone please explain the syntax of the following snippet from a c++ class template?
public:
explicit Foo(size_t ratio = 500)
:list(Bar<I>())
,vec(Bar<iterator>())
,n(0), r(ratio){}
I have decent experience with programming and understand most concepts but the syntax behind this is just so foreign to me, i can't understand it. I is the generic type. Specifically, I'm not sure what the explicit is doing here, in addition to what is going on when one would execute some code such as Foo<int> myfoo;.
thanks!
The explicit keyword makes sure that your constructor is not picked as part as a user-defined conversion sequence to perform implicit conversions when copy-initialized with a value of type size_t.
Without the explicit keyword, for instance, all of the following would be valid:
Foo<int> bar(Foo<int> f)
{
return 0; // OK
}
Foo<int> f = 1729; // OK
bar(42); // OK
Declaring your constructor as explicit prevents the initializations above from compiling.
What follows the constructor's signature is an initialization list, and it is used to construct the base class subobjects and data member subobjects of your class before the body of your constructor is entered.
When you do not use an initialization list, the data members of your class are default-constructed. This, however, may not be an option if your class has const data members, or data members of reference type, or data members of a class type which do not have a default constructor.
This is a constructor for the class Foo, but I'm sure you already knew that.
explicit says that this constructor must receive an argument of size_t. That is, one cannot pass in anything that would be implicitly casted to a type size_t (ex: int).
The rest of the things are part of the constructor's initializer list, which means the constructor initializes the data member list with Bar<I>(), etc.
This is the definition of a constructor for Foo. The constructor is explicit, which means that it won't be involved in implicit conversions. As an example consider:
void bar(Foo);
bar(10); // Error - would require an implicit conversion
Marking a constructor as explicit prevents such conversions.
Everything after the : is a member initialization list. It lists members of Foo by their names and the parentheses contain the expressions with which to initialize them. For example, list will be initialized with the temporary object created by Bar<I>(), n is initialized with 0, etc.
Related
Given a class like this:
class MyClass {
struct PrivateType {};
public:
static void func(PrivateType) {}
};
I can't do this because PrivateType is private:
MyClass::func(MyClass::PrivateType());
But I can do this:
MyClass::func({});
So I'm instantiating a private type, what looks like, outside of the class scope. Is that an implicit conversion from an initializer list? If so, when a parameter is implicitly converted, does the conversion take place in scope of the class i.e. as if it was part of the body of the function? Why is that and is that always the case?
{} is a syntax called braced-init-list. It doesn't have a type or value category and doesn't represent any object or value like an expression does. It therefore also doesn't really make sense to consider something like a conversion from a {} to exist.
There are specific rules separate from those for expressions as arguments on how overload resolution considers _braced-init-list_s as arguments.
There are also rules separate from those for initialization from parentheses when initializing from a braced-init-list. However, after overload resolution, the function parameter is always initialized simply as if the argument was the initializer for the parameter in a copy-initialization, meaning that the PrivateType parameter object, which I will name here private_type, will be initialized as if by
PrivateType private_type = {};
in a local variable definition. However, this initialization always happens in the context of the caller. All of this is true for expressions as function arguments as well.
The initialization with the type as defined in your example would resolve to aggregate initialization, because PrivateType is an aggregate class. Since there are no members or base classes to initialize, the initialization then doesn't do anything.
None of this considers whether PrivateType is accessible (private) from the context of the caller, because accessibility only applies when deciding whether a member can be named (where e.g. overload resolution to a member function, constructor or operator overload counts as named). You are never using the name PrivateType in the caller though. private does not prevent construction of a member class in any context.
It seems to me that aggregate initialization (of suitable types) is not considered a constructor that you can actually call (except a few cases.)
As an example, if we have a very simple aggregate type:
struct Foo {
int x, y;
};
then this obviously works:
auto p = new Foo {42, 17}; // not "Foo (42, 17)" though...
but this doesn't work on any compiler that I've tested (including latest versions of MSVC, GCC, and Clang):
std::vector<Foo> v;
v.emplace_back(2, 3);
Again, it seems to me that any code that wants to call the constructor for a type T (in this case, the code in vector::emplace_back that forwards the passed arguments to the c'tor of T,) cannot use aggregate initialization simply (it seems) because they use parentheses instead of curly braces!
Why is that? Is it just a missed feature (nobody has proposed/implemented it yet,) or there are deeper reasons? This is a little strange, because aggregate types by definition have no other constructor to make the resolution ambiguous, so the language could have just defined a default aggregate constructor (or something) that would have all the members as defaulted arguments.
Is it just a matter of syntax? If the implementation of vector::emplace_back in the above example had used placement new with curly braces instead of parentheses, would it have worked?
Note: I want to thank those comments who've pointed out the behavior of vector and emplace because their comments will be valuable to those who'll find this question using those keywords, but I also want to point out that those are just examples. I picked the most familiar and concise example, but my point was about explicitly calling the aggregate initializer in any code (or in placement new, more specifically.)
For what it's worth, P0960 "Allow initializing aggregates from a parenthesized list of values" does exactly what it says. It seems to have passed EWG and is on its way into C++20.
aggregate types by definition have no other constructor to make the resolution ambiguous
That is incorrect. All classes have default constructors, as well as copy/move constructors. Even if you = delete them or they are implicitly deleted, they still technically have such constructors (you just can't call them).
C++ being C++, there are naturally corner cases where even P0960 does the "wrong thing", as outlined in the paper:
struct A;
struct C
{
operator A(); //Implicitly convertible to `A`
};
struct A { C c; }; //First member is a `C`
C c2;
A a(c2);
The initialization of a is a case of ambiguity. Two things could happen. You could perform implicit conversion of c2 to an A, then initialize a from the resulting prvalue. Or you could perform aggregate initialization of a by a single value of type C.
P0960 takes the backwards compatible route: if a constructor could be called (under existing rules), then it always takes priority. Parentheses only invoke aggregate initialization if there is no constructor that could have been called.
https://en.cppreference.com/w/cpp/language/aggregate_initialization
Aggregate Initialization is not a constructor.
According to this document, you did not define any constructors and meet the other conditions for Aggregate Initialization. (Refer to the item "class type" in the section "Explanation") That means your code does not call something like automatically generated constructor of signature Foo(int, int) but it is just another feature.
The document says about its effect is:
Each array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
As vector::emplace_back(Args&&... args) works this way and it cannot find such constructor.
The arguments args... are forwarded to the constructor as std::forward<Args>(args)....
So it does not find such constructor.
Thinking this way, it also makes sense that your code cannot compile auto p = new Foo (42, 17);.
One more example, if you write any kind of constructor(even Foo::Foo() {}), auto p = new Foo {42, 17}; does not work. Because now it does not meet the condition for Aggregate Initialization.
As far as I know Aggregate Initialization also works in C which does not even support constructors.
Here's a good article worth reading.
I know the concept that we can call the Constructor both Explicitly and Implicitly, and i have tested both the scenarios(generally till now my all purposes got fulfilled by calling constructor Implicitly), but i was wondering that constructor get called implicitly whenever we create objects, so what is the main reason behind calling the constructor Explicitly. What advantage or disadvantage it provides when we call constructor Explicitly over the Implicit Call?
Example
class integer
{
int m ,n;
public:
integer (int x , int y);
};
integer :: integer (int x , int y )
{
m=x; n = y;
}
Now if i call like
integer int1 = integer( 0 , 100); // Explicit Call
integer int1(1,100); // implicit call
There are two different problems here, as your definition of explicit and implicit does not match the standard definition (on which most of the existing answers are based, being written before you added your example containing your own definition of explicit and implicit).
Ok so let's first consider your definition of explicit, which would be (I guess you call it explicit because you explicitly write the type name?):
integer int1 = integer(0, 100);
versus your definition of implicit which would be:
integer int1(1, 100);
In this case the first "explicit" call really doesn't have any advantage over the second "implicit" call. But there is still a difference. The first one actually creates a temporary using the two-argument constructor, which is then used to create int1 using the copy constructor. Although in practice the compiler will usually optimize away this additional copy, it still won't work if your copy constructor is private, whereas the second one only needs the two-argument constructor (you could even see this as disadvantage).
But now to the actual standard definitions of explicit and implicit. An explicit constructor call is any constructor call you, well, explicitly call. Practically speaking, whenever you use the parenthesis syntax () to create an object you explicitly call a constructor, otherwise it's an implicit constructor call (so to say, being done behind the scenes by the compiler):
integer int1; // implicit default constructor
integer int1(1, 100); // explicit two-arg constructor
integer int1 = integer(0, 100); // explicit two-arg constructor, implicit copy constructor
void func(integer); // function taking by-value
func(int1); // implicit copy constructor
So the only constructors that can be called implicitly are the default construtor and any one-argument constructors (including copy and move constructors). A special problem in this regard are one-argument constructors not being copy/move constructors:
struct integer
{
integer(int);
};
This allows the compiler to imlicitly call the the constructor to convert types, thus any int is implicitly convertible to integer:
void func(integer);
func(42); // implicit call to int-constructor
To disallow such behaviour you would have to mark the constructor explicit:
struct integer
{
explicit integer(int);
};
Which only allows it to be called explicitly (e.g. func(integer(42))) (but I guess you already knew this). This has the advantage that it doesn't introduce unnoticed/unwanted conversions behind the scenes, which can lead to all kinds of hard to find problems and ambiguities regarding overload resolution. It is therefore usual practice to mark any conversion constructors (one-argument non-copy/move constructors) explicit, and most probably also the reason why C++11 finally introduced explicit conversion operators.
So to sum up, according to your definition and example, there is really no advantage in using integer int1 = integer(1, 100); instead of integer int1(1, 100);, though it makes a (usually irrelevant) difference.
But according to the standard definitions, explicit constructor calls have plenty advantages over implicit ones, since the only way to actually construct an object explicitly is to use a, well, explicit constructor call, whereas implicit constructor calls are only done behind the scenes in certain situations and only work for zero- and one-argument constructors (as aschepler already pointed out). And explicitly marking conversion constructors as explicit has the advantage of disallowing unwanted implicit conversions behind the scenes.
Calling constructors explicitly allow you to construct object with arguments, rather than using the default constructor.
class Foo
{
public:
Foo() {}
Foo(int bar) : mBar(bar) {}
private:
int mBar;
}
Foo f; // Implicitly constructed with default constructor.
Foo f(7); // Explicitly constructed with argument for 'bar'
There are three ways a constructor can be called:
Implicitly, by declaring an instance of the type without initializing it
Also implicitly, by either initializing an instance with = or by causing an implicit conversion from the argument type to your class.
Explicitly calling the constructor, passing arguments.
Which of these you can use in a particular context depends on the constructors you're calling.
class Foo
{
Foo(); // 1
Foo(int a); // 2
explicit foo(const std::string& f); // 3
Foo(int c, int d); // 4
};
This constructor will be called implicitly when declaring Foo f;. Never attempt to call a constructor without arguments explicitly, as Foo f(); will declare a function!
This one can be called by writing Foo f = 42; or Foo f(42).
The explicit keyword forbids implicit conversion by writing Foo f = std::string("abc"); or function_taking_foo(function_returning_string());.
As there are multiple arguments, the explicit version is the only suitable.
I hate to say this, because it is so perverse, but there is an additional way to explicitly call the constructor.
class integer
{
int m ,n;
public:
integer (int x , int y);
};
integer :: integer (int x , int y )
{
m=x; n = y;
}
The constructor can be explicitly called on an already-constructed object.
integer i(1,100);
i.~integer();
i.integer::integer(2,200);
Here I've constructed (explicitly) an instance of integer. Then I've explicitly called its destructor. Then I've explicitly called the constructor again. I suppose you might use this idiom in testing. I am not aware of any place in the standard that forbids it. It works in Visual Studio 2010. I haven't tested a really wide range of compilers.
These calls are explicit for large values of 'explicit'.
If you make a function that takes a reference to a object of your class, and you pass it another type other than your object the constructor of your class will convert that type to an object of your class. Any one argument constructor is treated as a conversion constructor. If you declare that constructor explicit, then passing a different type other than your object to that function will not convert it and the compiler will return an error
Assume that the following code is legal code that compiles properly, that T is a type name, and that x is the name of a variable.
Syntax one:
T a(x);
Syntax two:
T a = x;
Do the exact semantics of these two expressions ever differ? If so, under what circumstances?
If these two expressions ever do have different semantics I'm also really curious about which part of the standard talks about this.
Also, if there is a special case when T is the name of a scalar type (aka, int, long, double, etc...), what are the differences when T is a scalar type vs. a non-scalar type?
Yes. If the type of x is not T, then the second example expands to T a = T(x). This requires that T(T const&) is public. The first example doesn't invoke the copy constructor.
After the accessibility has been checked, the copy can be eliminated (as Tony pointed out). However, it cannot be eliminated before checking accessibility.
The difference here is between implicit and explicit construction, and there can be difference.
Imagine having a type Array with the constructor Array(size_t length), and that somewhere else, you have a function count_elements(const Array& array). The purpose of these are easily understandable, and the code seems readable enough, until you realise it will allow you to call count_elements(2000). This is not only ugly code, but will also allocate an array 2000 elements long in memory for no reason.
In addition, you may have other types that are implicitly castable to an integer, allowing you to run count_elements() on those too, giving you completely useless results at a high cost to efficiency.
What you want to do here, is declare the Array(size_t length) an explicit constructor. This will disable the implicit conversions, and Array a = 2000 will no longer be legal syntax.
This was only one example. Once you realise what the explicit keyword does, it is easy to dream up others.
From 8.5.14 (emphasis mine):
The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see class.temporary, class.copy.
So, whether they're equivalent is left to the implementation.
8.5.11 is also relevant, but only in confirming that there can be a difference:
-11- The form of initialization (using parentheses or =) is generally insignificant, but does matter when the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.
T a(x) is direct initialization and T a = x is copy initialization.
From the standard:
8.5.11 The form of initialization (using parentheses or =) is generally insignificant, but does matter when the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.
8.5.12 The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form
T x = a;
The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization and is equivalent to the form
T x(a);
The difference is that copy initialization creates a temporary object which is then used to direct-initialize. The compiler is allowed to avoid creating the temporary object:
8.5.14 ... The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.
Copy initialization requires a non-explicit constructor and a copy constructor to be available.
In C++, when you write this:
class A {
public:
A() { ... }
};
The compiler actually generates this, depending on what your code uses:
class A {
public:
A() { ... }
~A() { ... }
A(const A& other) {...}
A& operator=(const A& other) { ... }
};
So now you can see the different semantics of the various constructors.
A a1; // default constructor
A a2(a1); // copy constructor
a2 = a1; // copy assignment operator
The copy constructors basically copy all the non-static data. They are only generated if the resulting code is legal and sane: if the compiler sees types inside the class that he doesn't know how to copy (per normal assignment rules), then the copy constructor won't get generated. This means that if the T type doesn't support constructors, or if one of the public fields of the class is const or a reference type, for instance, the generator won't create them - and the code won't build. Templates are expanded at build time, so if the resulting code isn't buildable, it'll fail. And sometimes it fails loudly and very cryptically.
If you define a constructor (or destructor) in a class, the generator won't generate a default one. This means you can override the default generated constructors. You can make them private (they're public by default), you can override them so they do nothing (useful for saving memory and avoiding side-effects), etc.
I wonder if construction like this (initialization list) has well defined EO (evaluation order):
struct MemoryManager
{
Pair* firstPair_;//<-beg
Pair* currentPair_;
Pair* lastPair_;//<-end
MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
{/*e.b.*/}
};
If yes I personally would prefer this way to the more conventional:
MemoryManager():firstPair_(nullptr),
currentPair_(nullptr),
lastPair_(nullptr)
{/*e.b*/}
As John Dibling has remarked, your construct is technically correct for the given concrete example, but it's brittle and it's hard to understand for many programmers.
Brittleness:
Can fail if order of declaration is changed.
Can fail if the init list is changed.
To evaluate such constructs on your own, keep this idea foremost in your mind: code is not about instructing the compiler to do your bidding, it is about communicating your intent to others (and perhaps your later self).
Hence, try to write clear code.
Cheers & hth.,
Yes. As shown in your code, the members will be initialized in the same order they are declared in the struct/class definition (the order of initializers in the constructor definition is irrelevant, at best you will get a warning telling you they are in an incorrect order).
12.6.2 §5: Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
Note that this is only true for variables that are part of the same access specifier, so for instance variables found in a public: specifier may be initialized before or after those found in a private: specifier (the struct counts as a public: specifier, of course).
EDIT: the above paragraph was incorrect, I was thinking of allocation, not initialization:
9.2 §12: Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (class.access.spec).
However, the more conventional way has a reason to exist, namely that if the order of declaration of the variables changes (for instance, due to refactoring), the code will not break silently. People don't readily assume the order of declaration is relevant, unless a warning tells them otherwise.
If you want to do this, then do it the way everybody understands immediately without having to browse the standard:
MemoryManager()
// no initialization here
{
lastPair_ = currentPair_ = firstPair_ = nullptr;
}
However, I don't really see what this buys you over
MemoryManager()
: lastPair_(), currentPair_(), firstPair_()
{}
which does exactly the same in only about half a dozen more characters.
For this specific example, the initialization order for members is irrelevant. The following constructor would have the same behaviour as the one in the question:
MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}
This is because the members are POD and so are not default initialized by the constructor at all (12.6.2/4 C++ '03):
If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non- static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
For the raw pointer members above, the 'otherwise' bullet applies.
Now, even if the members did have class type, say:
class MbrType {
public:
MbrType();
MbrType(int *);
MbrType(MbrType const &);
MbrType & operator=(MbrType const &);
};
Then, the constructor as you've written it would result in the members having the values that you expect, but a non optimizing compiler would be allowed to implement your constructor as:
MemoryManager()
: firstPair_ () // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}
Resulting in 6 calls rather than 3.
No, but it doesn't matter. Your code does not depend on the order in which currentPair_ and firstPair_ are zeroed.