157

Consider the following inlined function :

// Inline specifier version
#include<iostream>
#include<cstdlib>

inline int f(const int x);

inline int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

and the constexpr equivalent version :

// Constexpr specifier version
#include<iostream>
#include<cstdlib>

constexpr int f(const int x);

constexpr int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

My question is : does the constexpr specifier imply the inline specifier in the sense that if a non-constant argument is passed to a constexpr function, the compiler will try to inline the function as if the inline specifier was put in its declaration ?

Does the C++11 standard guarantee that ?

3
  • 10
    '[Will] the compiler try to inline the function' is not what the inline specifier does. (Or maybe I misunderstood your phrasing.)
    – Luc Danton
    Commented Jan 18, 2013 at 1:52
  • 12
    The inline specifier no longer has anything to do with inlining
    – K-ballo
    Commented Jan 18, 2013 at 1:53
  • 4
    The question foots on the wrong assumption that inline is directly related to inlining. So no, the constexpr specifier doesn't imply the inline specifier in that sense, as that sense doesn't exist. Commented Jan 18, 2013 at 9:37

2 Answers 2

181

Yes ([dcl.constexpr], §7.1.5/2 in the C++11 standard): "constexpr functions and constexpr constructors are implicitly inline (7.1.2)."

Note, however, that the inline specifier really has very little (if any) effect upon whether a compiler is likely to expand a function inline or not. It does, however, affect the one definition rule, and from that perspective, the compiler is required to follow the same rules for a constexpr function as an inline function.

I should also add that regardless of constexpr implying inline, the rules for constexpr functions in C++11 required them to be simple enough that they were often good candidates for inline expansion (the primary exception being those that are recursive). Since then, however, the rules have gotten progressively looser, so constexpr can be applied to substantially larger, more complex functions.

4
  • Given that the idea is that constant expressions are evaluated at compile time, I suppose most uses of constexpr functions won't cause any code generation at all...
    – Kerrek SB
    Commented Jun 11, 2014 at 0:05
  • 14
    @KerrekSB constexpr functions are potentially evaluated at compile time. However the C++14 standard is littered with ones which will very likely be called at runtime. For example: std::array<T,N>::at
    – Eponymous
    Commented Sep 30, 2014 at 18:22
  • 1
    @Eponymous yes but only the most-reduced form will remain as opcodes though. e.g: the bound checks, will be evaluated at build time, since their code path is const. But the returned value will be *(data+offset)
    – v.oddou
    Commented Apr 16, 2020 at 7:57
  • 1
    I suggest editing this to clarify that it's no longer necessarily the case as of C++17 as per the answer below this one. See open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r2.pdf
    – Tim Rae
    Commented Jan 23 at 8:47
84

constexpr does not imply inline for variables (C++17 inline variables)

While constexpr does imply inline for functions, it does not have that effect for variables, considering C++17 inline variables.

For example, if you take the minimal example I posted at: How do inline variables work? and remove the inline, leaving just constexpr, then the variable gets multiple addresses, which is the main thing inline variables avoid:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Compile and run:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

If you change:

inline constexpr int notmain_i = 42;

to:

constexpr int notmain_i = 42;

then the assert blows up:

main: main.cpp:7: int main(): Assertion `&notmain_i == notmain_func()' failed.
./test.sh: line 5: 93012 Aborted                 (core dumped) ./main

so each version of the notmain_i had a different address, even though both were constexpr. I.e. it is not a "C++17 inline variable".

constexpr static data members are implicitly inline

constexpr static data members are however implicitly inline, e.g.:

class C {
    static constexpr char *c = (char*)"abc";
};

only compiles with the constexpr, since static data members only make sense if they are "the same across all compilation units".

Note that this can still work without constexpr for const integer types: How to initialize private static data members in a header file but constexpr allows it to work with more types.

class D {
    const static int i = 42;
};

If you want the static member to be modifiable, then you need to use inline:

class E {
    inline static int i = 42;
};

Minimal example that constexpr implies inline for functions

As mentioned at: https://stackoverflow.com/a/14391320/895245 the main effect of inline is not to inline but to allow multiple definitions of a function, standard quote at: How can a C++ header file include implementation?

We can observe that by playing with the following example:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    assert(shared_func() == notmain_func());
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline int shared_func() { return 42; }
int notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

int notmain_func() {
    return shared_func();
}

Compile and run:

g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'notmain.o' 'notmain.cpp' 
g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.o' 'main.cpp' 
g++ -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.out' notmain.o main.o
./main.out

If we remove inline from shared_func, link would fail with:

multiple definition of `shared_func()'

because the header gets included into multiple .cpp files.

But if we replace inline with constexpr, then it works again, because constexpr also implies inline.

GCC implements that by marking the symbols as weak on the ELF object files: How can a C++ header file include implementation?

Tested in GCC 8.3.0.

11
  • 11
    BTW, a static class member variable declared constexpr is still inline. cppreference.com: A static member variable (but not a namespace-scope variable) declared constexpr is implicitly an inline variable.
    – anton_rh
    Commented Sep 3, 2019 at 14:36
  • @anton_rh thanks, I hadn't seen that rule, update answer. Commented Sep 3, 2019 at 15:34
  • 2
    @ciro-santilli-trump-ban-is-bad If you have a chance, can you correct your statement "constexpr static variables are however implicitly static."? I assume one of those words "static" was meant to be "inline".
    – jorgbrown
    Commented Jan 22, 2021 at 6:41
  • 1
    @SohailSi see: stackoverflow.com/questions/1143936/… Commented Nov 29, 2022 at 11:50
  • 1
    @CiroSantilliOurBigBook.com I think and hope so, at least I would not interpret anymore now that all static constexpr variables (which of course includes those at namespace scope) are implicitly inline. Thank you very much for your swift edit.
    – Brandlingo
    Commented Jun 10 at 19:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.