Should i define the default constructor? [closed] - c++

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
So we were doing some peer review, and this minor disagreement rose up,
Should the default constructor be defined even if it does nothing, or should we let the compiler define it?
So far, none of the sides could come up with any major advantages or disadvantages. What are the pros and cons of each style and which one is considered "cleaner"?

This is likely to be closed as "primarily opinion-based," but I can give you some objective points to consider:
If you don't define the default constructor, and someone later adds a constructor with parameters and forgets to also add the parameterless constructor, the default constructor will go away and that could break existing code. Explicitly defining it ensures that even if someone adds an overloaded constructor later, the parameterless one is still there.
If the constructor is declared in the header and defined out-of-line (in a .cc/.cpp file), then the implementation can later be modified with dependent code only needing to be re-linked. Declaring a constructor after the fact necessarily affects the header, requiring recompilation of dependent code.
An empty out-of-line constructor will still need to be called, incurring a small run-time cost, whereas with an implicitly provided default constructor the compiler can see that nothing needs to be done and avoid the call.
Defining it explicitly requires more typing and results in more lines of code. There is a small but nonzero cost associated with this (time taken to type it in, and time taken for readers of the code to read through it).
Defining it explicitly disqualifies the class from being an aggregate class, unless you use =default in C++11.
Yes, these points are contradictory. I think you will find that the prevailing opinion is not to define it explicitly, but as far as the language is concerned there is no correct or incorrect way. (Unless you need your type to be an aggregate.)

Without any user c'tors
You should only implement your own default constructor it it does anything else then the one the compiler would generate.
If you want to give the reader a hint, you can replace the implementation with = default in C++11.
struct MyObject {
// only members that the compiler initializes fine
std::vector<int> data_;
MyObject() = default;
};
or, if you don't wan to be that verbose (or before C++11):
struct MyObject {
// only members that the compiler initializes fine
std::vector<int> data_;
};
With other user c'tors
The compiler will not generate a default c'tor if you provide any other c'tor. in this case you should only provide a default c'tor if it makes sense, semantically -- not because it's "nice" to have one :-)
Pros and Cons
Explicitly providing an unnecessary default c'tor
(-) it is bad to have more code then necessary
(o) except when the gained clarity outweighs the longer source code (= default)
(-) a compiler-generated default c'tor will be close to optimal
(-) if you start providing an unnecessary member ("unnecessary", because it would be generated), you later end up defining all of the auto generated ones, i.e. default-c'tor, destructor, copy, move, assign and move-assign. You really don't want to end up there.
(-) will you know if you should mark the default c'tor with noexcept? The compiler often does. Don't waist your brain powers where the compiler can help you.
I really can not see any clear (+), but that's just me.

Related

What is the difference between setting the copy constructor to private and =delete? [duplicate]

This question already has answers here:
delete modifier vs declaring function as private
(4 answers)
Closed 3 years ago.
I have seen a lot of books recommend using =delete, is this just clear what it means? (making the program more readable) rather than saying that it is a bad thing to set the copy constructor to private?
Thinks your answers
class A {
A(const A&);
// some functions and variable
public:
// or you can A(const A&)=delete;
// do something
};
This is a relatively new functionality (added in the 2011 revision of C++) whose main motivation surely was readability and clarity of intent. However, the difference is more than just cosmetic.
Remember that with a constructor declared in the class, nothing prevents some other translation unit from actually providing the definition. It is quite usual to just list a class' member functions in a header file and implement them in a separate .cpp. If someone uses the copy constructor from inside the class, the compiler will complain that the definition is missing ("undefined reference to..."). If a naive programmer somehow reaches the conclusion that you forgot to implement it because you never needed it, they can go ahead and do so. Suddenly your class is copyable, even though only from within its own member functions (and friends). The =delete constructor prevents this, and the compiler errors are nicer (usually along the lines of "the object can't be copied because the copy constructor was declared as deleted" rather than "undefined reference to ..." or "A::A is private within this context").

Default member values or default constructor parameters in structures?

In modern C++ I am allowed to implement a struct with default member values i.e.
struct A
{
int x = 5;
float y = 1.0f;
};
but I can also do create a structure that has no "default member values" but can its constructor can be called with default parameters, as in the example below:
struct B {
int x;
float y;
B(int x_ = 5, float y_ = 1.0f) : x(x_), y(y_) {}
};
What I want to know is whether there is any difference from the clean code or architecture point of view between those? Or maybe there are another, even more important differences? In the first case, I've got less amount of code to write and I believe I can still construct the object like A({2, 3.14f}) even if the constructor is not defined.
Which would be your way to go in a project and why?
There is no "right" way do choose a form of initialization, it depends on the specific constraints/circumstances of a project or even your personal favor. However, I think those issues should be taken into account when deciding pro/contra in-class initializers.
Pro: Readability. When you want to understand what a class is good for, you often start off with its definition. When going through the data members, it's comfortable for your brain to have the initial value of each data member right next to its type, instead of scanning possibly multiple constructors.
Pro: Adding more constructors is much easier when no copy-pasting of the initializers of another existing constructor is required. With in-class member initializers, you are less likely to forget some initialization when adding a constructor.
Pro: Removing a constructor is easy. Let's say you start with a new class, add a bunch of member functions, a constructor and data members. Then you remember what Scott Meyers said in "How Non-Member Functions Improve Encapsulation" and you decide to turn the member functions into free ones. Your type now looks like a dumb struct with some data packed together - and when its constructor doesn't involve any business logic, you might want to turn the type into an aggregate: with in class initializers, you just remove the constructor - done.
Contra: Whenever you go with in class initializers, the concrete type of the data member being initialized must be known. This can be in issue when the class definition in question is in a header (most likely), compile times are an issue (most likely), the data member in question has a heavy-weight definition (sometimes) and/or requires non-trivial dependencies upon construction (sometimes). A simple guideline could be: in a scenario where the Pimpl-idiom is helpful, it's more important than points 1.-3. and does outrule in-class initializers, so go with Pimpl then.
Final note: the core guidelines recommend using in-class initializers in C.48, with the reasoning:
Makes it explicit that the same value is expected to be used in all constructors. Avoids repetition. Avoids maintenance problems. It leads to the shortest and most efficient code.

Automatically determine if user-defined function is equivalent to the implicit one

Sometimes, users implement functions with the equivalent functionality as their implicitly defined versions. For example, a copy constructor which simply calls the copy constructor of all its members.
struct A
{
int B;
A(const A& a) : B(a.B) { }
}
This is undesirable, because it causes additional maintenance, for example if the class members are renamed/reordered, etc., and reduces readability. Also, adding these functions also means that functions such as std::is_trivially_copy_constructable claim the type is cannot be trivially copy constructed (but in practice, it actually could be).
I have a code base where this seems to be a common occurrence, which I would like to rectify, by deleting these implementations. However, I am uneasy about removing functionality that seems to be identical to implicit implementation, in case it might not actually be equivalent. Is there a method for determining whether a function is equivalent to its implicit version? (Using any toolset/language variation/etc is acceptable).
My suggestion is to not try to programmatically determine if these functions are the same as the default implementation, because the difference might actually be a mistake (and they were supposed to have the normal default behavior).
Instead I would just suggest to write up a set of unit tests that take care of testing the expected behavior of the various functions, and then make sure they pass on the default implementations. Then not only do you have a test framework for future enhancements you can be confident the functions did what you wanted.

Will using brace-init syntax change construction behavior when an initializer_list constructor is added later?

Suppose I have a class like this:
class Foo
{
public:
Foo(int something) {}
};
And I create it using this syntax:
Foo f{10};
Then later I add a new constructor:
class Foo
{
public:
Foo(int something) {}
Foo(std::initializer_list<int>) {}
};
What happens to the construction of f? My understanding is that it will no longer call the first constructor but instead now call the init list constructor. If so, this seems bad. Why are so many people recommending using the {} syntax over () for object construction when adding an initializer_list constructor later may break things silently?
I can imagine a case where I'm constructing an rvalue using {} syntax (to avoid most vexing parse) but then later someone adds an std::initializer_list constructor to that object. Now the code breaks and I can no longer construct it using an rvalue because I'd have to switch back to () syntax and that would cause most vexing parse. How would one handle this situation?
What happens to the construction of f? My understanding is that it will no longer call the first constructor but instead now call the init list constructor. If so, this seems bad. Why are so many people recommending using the {} syntax over () for object construction when adding an initializer_list constructor later may break things silently?
On one hand, it's unusual to have the initializer-list constructor and the other one both be viable. On the other hand, "universal initialization" got a bit too much hype around the C++11 standard release, and it shouldn't be used without question.
Braces work best for like aggregates and containers, so I prefer to use them when surrounding some things which will be owned/contained. On the other hand, parentheses are good for arguments which merely describe how something new will be generated.
I can imagine a case where I'm constructing an rvalue using {} syntax (to avoid most vexing parse) but then later someone adds an std::initializer_list constructor to that object. Now the code breaks and I can no longer construct it using an rvalue because I'd have to switch back to () syntax and that would cause most vexing parse. How would one handle this situation?
The MVP only happens with ambiguity between a declarator and an expression, and that only happens as long as all the constructors you're trying to call are default constructors. An empty list {} always calls the default constructor, not an initializer-list constructor with an empty list. (This means that it can be used at no risk. "Universal" value-initialization is a real thing.)
If there's any subexpression inside the braces/parens, the MVP problem is already solved.
Retrofitting classes with initializer lists in updated code is something that sounds like it will be a common thing to happen. So people start using {} syntax for existing constructors before the class is updated, and we want to automatically catch any old uses, especially those used in templates where they may be overlooked.
If I had a class like vector that took a size, then arguably using {} syntax is "wrong", but for the transition we want to catch that anyway. Constructing C c1 {val} means take some (one, in this case) values for the collection, and C c2 (arg) means use val as a descriptive piece of metadata for the class.
In order to support both uses, when the type of element happens to be compatible with the descriptive argument, code that used C c2 {arg} will change meaning. There seems to be no way around it in that case if we want to support both forms with different meanings.
So what would I do? If the compiler provides some way to issue a warning, I'd make the initializer list with one argument give a warning. That sounds tricky not to mention compiler specific, so I'd make a general template for that, if it's not already in Boost, and promote its use.
Other than containers, what other situations would have initializer list and single argument constructors with different meanings where the single argument isn't something of a very distinct type from what you'd be using with the list? For non-containers, it might suffice to notice that they won't be confused because the types are different or the list will always have multiple elements. But it's good to think about that and take additional steps if they could be confused in this manner.
For a non-container being enhanced with initializer_list features, it might be sufficient to specifically avoid designing a one-argument constructor that can be mistaken. So, the one-arg constructor would be removed in the updated class, or the initializer list would require other (possibly tag) arguments first. That is, don't do that, under penalty of pie-in-face at the code review.
Even for container-like classes, a class that's not a standard library class could impose that the one-arg constructor form is no longer available. E.g. C c3 (size); would have to be written as C c3 (size, C()); or designed to take an enumeration argument also, which is handy to specify initialized to one value vs. reserved size, so you can argue it's a feature and point out code that begins with a separate call to reserve. So again, don't do that if I can reasonably avoid it.

Why can I not implement default constructors for structs in D?

Writing code like
struct S
{
this() // compile-time error
{
}
}
gives me an error message saying
default constructor for structs only allowed with #disable and no body.
Why??
This is one of cases much more tricky than one can initially expect.
One of important and useful features D has over C++ is that every single type (including all user types) has some initial non-garbage value that can be evaluated at compile-time. It is used as T.init and has two important use cases:
Template constraints can use T.init value to check if certain operations can be done on given type (quoting Kenji Hara's snippet):
template isSomething(T) {
enum isSomething = is(typeof({
//T t1; // not good if T is nested struct, or has #disable this()
//T t2 = void; auto x = t2; // not good if T is non-mutable type
T t = T.init; // avoid default construct check
...use t...
}));
}
Your variables are always initialized properly unless you explicitly use int i = void syntax. No garbage possible.
Given that, difficult question arises. Should we guarantee that T() and T.init are the same (as many programmers coming from C++ will expect) or allow default construction that may easily destroy that guarantee. As far as I know, decision was made that first approach is safer, despite being surprising.
However, discussions keep popping with various improvements proposed (for example, allowing CTFE-able default constructor). One such thread has appeared very recently.
It stems from the fact that all types in D must have a default value. There are quite a few places where a type's init value gets used, including stuff like default-initializing member variables and default-initializing every value in an array when it's allocated, and init needs to be known at compile for a number of those cases. Having init provides quite a few benefits, but it does get in the way of having a default constructor.
A true default constructor would need to be used in all of the places that init is used (or it wouldn't be the default), but allowing arbitrary code to run in a number of the cases that init is used would be problematic at best. At minimum, you'd probably be forced to make it CTFE-able and possibly pure. And as soon as you start putting restrictions like that on it, pretty soon, you might as well just directly initialize all of the member variables to what you want (which is what happens with init), as you wouldn't be gaining much (if anything) over that, which would make having default constructors pretty useless.
It might be possible to have both init and a default constructor, but then the question comes up as to when one is used over the other, and the default constructor wouldn't really be the default anymore. Not to mention, it could become very confusing to developers as to when the init value was used and when the default constructor was used.
Now, we do have the ability to #disable the init value of a struct (which causes its own set of problems), in which case, it would be illegal to use that struct in any situation that required init. So, it might be possible to then have a default constructor which could run arbitrary code at runtime, but what the exact consequences of that would be, I don't know. However, I'm sure that there are cases where people would want to have a default constructor that would require init and therefore wouldn't work, because it had been #disabled (things like declaring arrays of the type would probably be one of them).
So, as you can see, by doing what D has done with init, it's made the whole question of default constructors much more complicated and problematic than it is in other languages.
The normal way to get something akin to default construction is to use a static opCall. Something like
struct S
{
static S opCall()
{
//Create S with the values that you want and return it.
}
}
Then whenever you use S() - e.g.
auto s = S();
then the static opCall gets called, and you get a value that was created at runtime. However, S.init will still be used any place that it was before (including S s;), and the static opCall will only be used when S() is used explicitly. But if you couple that with #disable this() (which disables the init property), then you get something akin to what I described earlier where we might have default constructors with an #disabled init.
We may or may not end up with default constructors being added to the language eventually, but there are a number of technical problems with adding them due to how init and the language work, and Walter Bright doesn't think that they should be added. So, for default constructors to be added to the language, someone would have to come up with a really compelling design which appropriately resolves all of the issues (including convincing Walter), and I don't expect that to happen, but we'll see.