Artyer
Artyer

Reputation: 40836

Avoid compiling definition of inline function multiple times

I have a non-template struct in a header file:

struct X {
    constexpr X() : /* ... */ { /* ... */ }
    constexpr void f() {
        // ...
    }
};

With functions of varying size. This is used in a lot of different translation units, and each function appears in multiple object files for them to be discarded in the final executable.

What I want is for the definitions to be in a single object file, and the other translation units can either inline the function or use an external definition (something like the extern inline semantics from C). How can I do that?

It seems to work with templates and extern template:

namespace detail {

template<std::nullptr_t>
struct X_base {
    constexpr X_base() // ...
    constexpr void f() // ...
};

extern template struct X_base<nullptr>;

}

struct X : detail::X_base<nullptr> {
    using X_base::X_base;
};

// X.cpp
#include <X.hpp>

template struct detail::X_base<nullptr>;

But are there any major downsides to this (longer symbol names, confusing to read, needs documentation, etc.), or are there any easier ways to do this?

Upvotes: 3

Views: 212

Answers (1)

Davis Herring
Davis Herring

Reputation: 39838

C++ doesn’t have the notion of an inline function that must be emitted in one translation unit and which therefore certainly need not be emitted anywhere else. (It doesn’t have the notion of emitting object code at all, but the point is that there’s no syntax that says “I promise this definition is ODR-identical to the others except that it and only it bears this marker.” so that compilers could do that.)

However, the behavior you want is the obvious way of implementing C++20 modules: because the definition of an inline function in a module is known to be the only definition, it can and should be emitted once in case several importing translation units need an out-of-line copy of it. (Inlining is still possible because the definition is made available in a compiler-internal form as part of building the module.) Bear in mind that member functions defined in a class in a module are not automatically inline, although constexpr still implies it.

Another ugly workaround is to make non-inline wrappers to be used outside of constant evaluation, although this could get unwieldy if there were multiple levels of constexpr functions that might also be used at runtime.

Upvotes: 2

Related Questions