Defaulted special member function with external definition - c++

I would like to declare a class with a defaulted constructor1, but I want to this defaulted constructor to be "extern" in the sense the constructor body should only be compiled once, when the TU for the class is compiled, and not in each TU that includes that header. Finally, the class should should not be seen to have a user-provided constructor, but rather a defaulted one2.
For example, here's one attempt:
foo.hpp:
class Foo {
public:
Foo();
};
foo.cpp:
Foo::Foo() = default;
This results in a defaulted constructor implementation, but it means that Foo has a user-provded constructor, which affects the traits of the type, e.g., whether it is trivial, etc.
Here's another way:
foo.hpp:
class Foo {
public:
Foo() = default;
};
This preserves the "not user-provided" property, but now every TU that includes this class and uses the constructor will generate machine code for it (and this code might get inlined, etc).
1 Really, this applies to any special member functions, but I'm using the default constructor here for concreteness.
2 Whether the class has a user-provided constructor affects various important traits and behaviors of the class, such as whether it has a trivial constructor, whether other special member functions are implicitly deleted or not, etc.

There’s no way in the language to tell the compiler that a function is non-user-provided (“you know everything about it”) while also claiming that it’s opaque (“you can’t do the code generation here”). Fortunately, the obvious implementation strategy for C++20 modules has that effect: a member function defaulted in its class in a module interface unit will be considered for inlining everywhere but obviously needs only one out-of-line copy in the object file generated for the module.
If you want a stronger guarantee than that, you’re well into implementation-specific territory and you should look into compiler-specific attributes to govern code generation.

Related

Unforgettable Factory: Doesn't work with default constructible objects

I am referring to this excellent blog post regarding the "Unforgettable Factory Registration Pattern": http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
I've noticed that this stops working when the self-registering types are default constructible. In the example provided by the code, the self-registering types (Animal base class) defines a constructor with an int parameter. If I slightly modify the code to make Animal become default constructible, the data map in the factory remains empty and objects of type Cat and Dog can no longer be constructed through the factory.
As far as I understand the issue lies in the fact that the Animal class no longer calls the registerT function. However, I fail to understand why that is and what modification(s) are necessary to make it work with default constructible classes.
Can somebody shed some light on this?
Useful:
Modified example: http://coliru.stacked-crooked.com/a/55fe8edc094c88a8
Original example: http://coliru.stacked-crooked.com/a/11473a649e402831
The int argument is a red herring. When changing the argument list you modified
Dog(int x) : m_x(x) {}
to be
Dog() = default;
From a user-provided constructor, to a defaulted one.
In effect, this means the compiler (I checked Clang and GCC) doesn't emit a function definition for Dog::Dog(), so it doesn't odr-use Animal::Registrar<Dog>::Registrar<Dog>(), which means its definition isn't instantiated (member function bodies only are only instantiated when odr-used). And so we never odr-use its registered static data member.
If the constructor is defaulted, then we never actually end up registering the factory function into the map. And indeed, after it becomes user-provided again, things start to work.
I am however unable to understand why that makes a difference. The user-provided constructor is still an inline member function. And inline function definitions are tied to odr-usage too. But perhaps that should be the subject of another question.

Is it possible to implement 'override'-like functionality in C++03

In C++11 we have override specifier that allows to validate in compile time that virtual function actually overrides behavior of interface in the base class.
Is there anyway to get the same behavior in C++03?
To check if a function foo exists in a base class Foo say, write
sizeof(&Foo::foo);
in the child class version of foo. The idea is that compilation will fail if the base class Foo::foo is not present, and has a benign effect if it is.
Of course, this has drawbacks - you can't descriminate by overloads for example. Also, static functions and member variables with a similar name will also match.

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.

Forward declare class specifying size

Is it possible to forward declare a class, and actually specify its size, such that in that same header I can use the class in a class definition?
For instance something like:
class Foo = 3 * sizeof(int);
class Bar
{
Foo foo;
};
Instead of having to #include "Foo.h", where that would be something like:
class Foo
{
int a, b, c;
};
FYI: no worries, I'd never want to do something like this (hopefully), I'm just being curious.
Note that for forward declaring enum classes the above is possible, which sounds a bit similar.
No this is not possible.
Why not?
Note that the compiler needs the complete type for instantiation an object not only for knowing the size but also to know which constructors and destructors have to be used, in your example this would only be a problem if a constructor or destructor has to be generated for Bar. There may also be alignment restrictions for Foo on a given platform.
In your case the compiler may have to generate default constructor(s) and a destructor for Bar. If Foo happens to have a non-trivial constructor and/or destructor, default or explicitly specified, that must be called in those constructors/destructors.
Comments have already mentioned that for enum classes, the complete type is in fact specified sufficiently by the forward declaration: the enum cannot have a constructor or destructor and its size and alignment restrictions are also known from the underlying integer type.
I presume the decision not to allow this was made because if class definitions could be spread around in different files, ensuring consistency would make the build process more complex and different from C. Note that C++ can be linked with rather "dumb" linkers, just like C, not actually caring about types and just filling in the right addresses for symbols (I'm simplifying a bit of course).
Not checking consistency would be very risky indeed if we could specify the size of a class in one file and then forget updating it when we add a member in another. The way it is the "one-definition-rule" says basically that anything can happen if a class has more than one non-identical definition in a program.

Are the default constructor and destructor ever inline?

I'm curious if the default constructor and destructor that the compiler generates are inline or not, because I can justify it either way. On the one hand, you want the default constructor/destructor to not be inline so that adding them later doesn't break ABI (because object files compiled when only the defaults were there will have inlined the generated definitions instead of what you define). On the other hand, for a C++ compiler to compile C code that performs as well as when compiled with a C compiler, it can't be adding constructor/destructor calls for every allocated struct, and in C++ the only functional difference between a class and a struct is supposed to be the default access protection. Maybe the linker addresses this somehow? Maybe the answer varies across compilers?
A consequence of this question: if I have a POD struct in C++, can I theoretically benefit under some compilers by defining empty inline constructor/destructors myself in place of the defaults?
The C++ standard says, in 12.1[class.ctor]/5
An implicitly-declared default constructor is an inline public member of its class
and in 12.4[class.dtor]/3
An implicitly-declared
destructor is an inline public member of its class.
if I have a POD struct in C++, can I theoretically benefit under some compilers by defining empty inline constructor/destructors myself in place of the defaults?
Theorotically, Yes! Any function(including constructors & destructors) can be declared inline, and putting the function body in the class definition is one way of doing that. However, it's up to the compiler if it actually does inline the function.
It varies across compilers, but in general: yes, they should.
With gcc at least, you get both an inline and an out-of-line function generated. The out-of-line version is marked as "link once", so no matter how many objects generate a default constructor, at most only one version will end up in the linked output. If in fact nobody uses the default constructor out-of-line, it's not included in the linked output at all, and you have effectively a purely inline function.