Reputation: 60451
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 ?
Upvotes: 162
Views: 70477
Reputation: 384074
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(¬main_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 ¬main_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 `¬main_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.
Upvotes: 94
Reputation: 490518
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.
Upvotes: 184