Identify automatically generated member functions - c++

Special member functions are "functions which the compiler will automatically generate if they are used, but not declared explicitly by the programmer".
http://en.wikipedia.org/wiki/Special_member_functions
Details are in §12 of the C++11 Standard:
The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor and move assignment operator (12.8), and destructor (12.4) are special member functions. [Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are odr-used (3.2). See 12.1,12.4 and 12.8. —end note]
What known methods can identify all generated special member functions upon compilation?
My preferred compilers are gcc and clang.

In c++11, header <type_traits> defines a set of following functions:
is_constructible
is_default_contructible
is_copy_contructible
is_move_contructible
is_assignable
is_copy_assignable
is_move_assignable
is_destructible
You can use them to test for existence of implicitly generated methods at compile time,
eg.:
std::is_constructible<ClassName>::value

Related

std::is_constructible for aggregates with invalid default initializer

What std::is_constructible shall return for an aggregate type that does not allow creation of objects due to invalid initializer of a member field?
Consider for example
#include <type_traits>
template <class T>
struct A {
T x{};
};
static_assert( !std::is_constructible_v<A<int&>> );
A<int&> obj; is not well-formed since it cannot initialize int& from {}. So I would expect that the example program above compiles fine, as it does in GCC. But MSVC accepts the opposite statement static_assert( std::is_constructible_v<A<int&>> ); presumable since the default constructor of A was not formally deleted. And Clang behaves in the third way stopping the compilation with the error:
error: non-const lvalue reference to type 'int' cannot bind to an initializer list temporary
T x{};
^~
: note: in instantiation of default member initializer 'A<int &>::x' requested here
struct A {
^
Online demo: https://gcc.godbolt.org/z/nnxcGn7WG
Which one of the behaviors is correct according to the standard?
std::is_constructible_v<A<int&>> checks whether a variable initialization with () initializer is possible. That's never aggregate initialization (not even in C++20), but always value initialization, which will use the default constructor.
Because your class doesn't declare any constructor, it still has an implicit default constructor declared. That one will be called during value initialization.
The implicit default constructor is also not defined as deleted, because none of the points in [class.default.ctor]/2 apply. There is one point for reference members without any default member initializer though.
So this constructor will be chosen in overload resolution and therefore std::is_constructible_v<A<int&>> is true. That the instantiation of the default constructor might be ill-formed is irrelevant. Only declarations are checked (the immediate context). So GCC's behavior is not correct.
The only remaining question now is whether the implicit default constructor (or rather the default member initializer?) is supposed to be instantiated from the std::is_constructible test itself, causing the program to be ill-formed.
As far as I can tell the standard doesn't specify that clearly. The standard says that the noexcept-specifier of a function is implicitly instantiated when it is needed. (see [temp.inst]/15)
It also says that it is needed if it is used in an unevaluated operand in a way that would be an ODR use if it was potentially-evaluated. (see [except.spec]/13.2
Arguably the type trait has to make such a use.
Then [except.spec]/7.3 specifies that it needs to be checked whether the default member initializers are potentially-throwing in order to check whether the implicit default constructor has a potentially-throwing exception specification.
Clang seems to then follow the idea that this requires instantiation of the default member initializer and therefore causes the compilation error because that instantiation is invalid.
The problems I see with that is:
I don't see anything in the [temp.inst] about when default member initializers are instantiated or what that would mean exactly.
[temp.inst]/15 speaks of the noexcept-specifier grammar construct and since the default constructor is implicit, that doesn't really work out.
The standard states:
Only the validity of the immediate context of the variable initialization is considered.
and
The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on.
Such side effects are not in the “immediate context” and can result in the program being ill-formed.
It is the generation of an the definition for an implicity-defined function (the constructor) that fails in your example. So this is not the "immediate context". That would allow:
The msvc interpretation: It doesn't consider the validity of the non-immediate context
The clang interpretation: The side effects result in your program being ill-formed
If anything it seems to me that gcc's result might be incorrect. But I'm not sure if the standard wants to forbid the consideration of the "non-immediate context" or if it simply doesn't require it.

C++ implicit definition of special functions

In the current version of the C++ draft (september 2019), paragraph [class.default.ctor]/4 states:
A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]) to create an object of its class type ([intro.object]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration. [...]. Before the defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its non-static data members shall have been implicitly defined. [ Note: An implicitly-declared default constructor has an exception specification ([except.spec]). An explicitly-defaulted definition might have an implicit exception specification, see [dcl.fct.def]. — end note ]
[class.dtor]/11 specifies a similar restriction for default destructors.
What does the highlighted sentence mean? Is it a restriction on the program or on the implementation (compiler)?
The first sentence of the quoted paragraph states when a defaulted default constructor is implicitly defined (e.g. when it is odr-used). If the highlighted sentence is a restriction on the program, then the following example should be ill-formed, because at (1) the defaulted default constructor of B is odr-used, therefore it is implicitly defined. However, at this point, the defaulted default constructor of A has not been implicitly defined, therefore the restriction in the highlighted sentence is not respected. This is because I believe that the defaulted default constructor of A is odr-used only after the defaulted default constructor of B is defined. Is this assumption wrong?
struct A {};
struct B : A {};
int main()
{
B b; // (1)
}
Thank you.
I agree with the OP that the drafting is sloppy, but I believe that the intent is clear:
When the implementation implicitly defines B's default constructor, if A's default constructor hasn't been implicitly defined yet in this translation unit, then the compiler should generate the implicit definition of A's default constructor first.
In response to the OP's concern that this creates an additional point where an implicit definition might be generated (besides what is stated in the first sentence of the quoted paragraph), sure, that's one possible way to interpret it. Another way to interpret it is that the compiler starts defining A's default constructor while it's in the middle of defining B's default constructor (because B's default constructor calls A's default constructor "to create an object of its class type", thus requiring A's default constructor to be implicitly defined) and that the former must be completed before the latter. In other words, the first sentence of the quoted paragraph refers to when the generation of the implicit definition begins, and the bolded sentence refers to when it is completed. I think these two interpretations are equivalent, so it's not necessary to choose between them.

Are copy constructors defined implicitly always, or only when they are used?

Consider the following code:
#include <memory>
#include <vector>
class A
{
private:
std::vector<std::unique_ptr<int>> _vals;
};
int main()
{
A a;
//A a2(a);
return 0;
}
Compiler A compiles this without issue unless I uncomment out the line A a2(a); at which point it complains about the copy constructor for std::unique_ptr being deleted, and therefore I can't copy construct A. Compiler B, however, makes that complaint even if I leave that line commented out. That is, compiler A only generates an implicitly defined copy constructor when I actually try to use it, whereas compiler B does so unconditionally. Which one is correct? Note that if I were to have used std::unique_ptr<int> _vals; instead of std::vector<std::unique_ptr<int>> _vals; both compilers correctly implicitly delete both copy constructor and assignment operator (std::unique_ptr has a explicitly deleted copy constructor, while std::vector does not).
(Note: Getting the code to compile in compiler B is easy enough - just explicitly delete the copy constructor and assignment operator, and it works correctly. That isn't the point of the question; it is to understand the correct behavior.)
From [class.copy.ctor]/12:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration.
A's copy constructor is defaulted, so it's implicitly defined only when it is odr-used. A a2(a); is just such an odr-use - so it's that statement that would trigger its definition, that would make the program ill-formed. Until the copy constructor is odr-used, it should not be defined.
Compiler B is wrong to reject the program.
Note: My answer is based on your comment:
[...] it's only on Windows, and only when I explicitly list class A as a DLL export (via, e.g., class __declspec(dllexport) A) that this happens. [...]
On MSDN we can learn that declaring a class dllexport makes all members exported and required a definition for all of them. I suspect the compiler generates the definitions for all non-deleted functions in order to comply with this rule.
As you can read here, std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value is actually true and I would expect the supposed mechanism (that defines the copy constructor in your case for export purposes) checks the value of this trait (or uses a similar mechanism) instead of actually checking whether it would compile. That would explain why the bahviour is correct when you use unique_ptr<T> instead of vector<unique_ptr<T>>.
The issue is thus, that std::vector actually defines the copy constructor even when it wouldn't compile.
Imho, a is_copy_constructible check is sufficient because at the point where your dllexport happens you cannot know whether the implicit function will be odr-used at the place where you use dllimport (possibly even another project). Thus, I wouldn't think of it as a bug in compiler B.
While I can't confirm this behavior (I do not have access to Windows compiler, and OP claims the bug happens with icc on Windows platform), taking the question at it's face value, the answer is - compiler B has a gross bug.
In particular, implicitly-declared copy constructor is defined as deleted, when ...
T has non-static data members that cannot be copied (have deleted,
inaccessible, or ambiguous copy constructors);
https://en.cppreference.com/w/cpp/language/copy_constructor
Thus, conforming compiler must semantically generate deleted copy-constructor, and successfully compile the program since such constructor never called.

Compiler generated methods

When declaring a class/struct/union the compiler will generate the default methods (rule of three). This also will happen when = default'ing these methods.
How do the default methods exactly look like?
For each of these methods, the compiler defines default inline methods that call the default methods of each attribute of the object. (so a pointer won't be initialized neither any built-in type).
For example let consider the default constructor. According to the C++ Standard
An implicitly-declared default constructor is an inline public member
of its class.
and
The implicitly-defined default constructor performs the set of
initializations of the class that would be performed by a user-written
default constructor for that class with no ctor-initializer (12.6.2)
and an empty compound-statement.
So it looks like
struct A
{
A() {}
};
except that it is not explicitly declared and defined.
About copy constructor you can read at my personal forum
http://cpp.forum24.ru/?1-1-0-00000021-000-0-0-1388485669
Though it is written in Russian but you can translate it for example using google service translate.
Those method will do the minimum necessary to initialize the class.
Default construtor - do nothing with simple members but call the constructors of more complex members (class/struct) as well as call the ctor of its super class.
Copy constructor will perform a shallow copy (memcpy).

Are undeclared copy-constructors automatically inline?

Are undeclared (auto-generated) copy constructors automatically marked as inline?
If so, and if I don't want them to be marked as inline, does that mean I have to define one manually and copy every single member I need by hand (assuming I'm not using C++11, so that there's no = default to take advantage of)?
They're treated as if they were declared inline (which doesn't
necessarily mean that they will be inlined). And yes, in
pre-C++11, the only way to prevent their being inline was to
declare and define them manually, copying every member and every
base class explicitly in the initializer list.
Yes. From C++11, 12.8/11:
An implicitly-declared copy/move constructor is an inline public member of its class.
I would strongly suggest reading all of 12.8 if you like to get more familiar with copy and move constructors.
They are, I believe. However, for a such a compiler-defined function, the difference between inline and not is non-observable. And yes, you would have to define your own for it to be non-inline, although why you would want such a thing is beyond me. It makes no difference to the semantics and won't affect the compiler's inlining.
Implicitly defined special member functions are inline and they must be as they can be implicitly generated in multiple translation units. The meaning of inline is that it can be defined in multiple translation units without violating the ODR, not that the code will actually be inlined (this depends on the type and the compiler).
Why don't you want the copy constructor to be inline?