Constexpr implique-t-il en ligne?

105

Considérez la fonction intégrée suivante:

// 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]));
}

et la version équivalente de constexpr:

// 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]));
}

Ma question est la suivante: le constexprspécificateur implique-t-il le inlinespécificateur dans le sens où si un argument non constant est passé à une constexprfonction, le compilateur essaiera inlinela fonction comme si le inlinespécificateur était placé dans sa déclaration?

La norme C ++ 11 garantit-elle cela?

Vincent
la source
5
«[Will] le compilateur essaiera d'insérer la fonction» n'est pas ce que fait le inlinespécificateur. (Ou peut-être que j'ai mal compris votre formulation.)
Luc Danton
5
Le inlineprescripteur n'a plus rien à voir avec l' inlining
K-ballo
2
La question repose sur la fausse hypothèse qui inlineest directement liée à l'inlining. Donc non, le constexprspécificateur n'implique pas le inlinespécificateur dans ce sens, car ce sens n'existe pas.
Christian Rau

Réponses:

139

Oui ([dcl.constexpr], §7.1.5 / 2 dans le standard C ++ 11): "Les fonctions constexpr et les constructeurs constexpr sont implicitement en ligne (7.1.2)."

Notez, cependant, que le inlinespécificateur a vraiment très peu (le cas échéant) d'effet sur le fait qu'un compilateur est susceptible de développer une fonction en ligne ou non. Cependant, cela affecte la règle de définition unique et, de ce point de vue, le compilateur doit suivre les mêmes règles pour une constexprfonction en tant que inlinefonction.

Je devrais également ajouter que, indépendamment de l' constexprimplication inline, les règles pour les constexprfonctions en C ++ 11 exigeaient qu'elles soient suffisamment simples pour qu'elles soient souvent de bons candidats pour l'expansion en ligne (la principale exception étant celles qui sont récursives). Depuis lors, cependant, les règles se sont progressivement assouplies et constexprpeuvent donc être appliquées à des fonctions beaucoup plus grandes et plus complexes.

Jerry Coffin
la source
Étant donné que l'idée est que les expressions constantes sont évaluées au moment de la compilation, je suppose que la plupart des utilisations de constexprfonctions ne provoqueront aucune génération de code ...
Kerrek SB
11
Les constexprfonctions @KerrekSB sont potentiellement évaluées au moment de la compilation. Cependant, le standard C ++ 14 est jonché de ceux qui seront très probablement appelés à l'exécution. Par exemple:std::array<T,N>::at
Eponymous
@Eponyme oui, mais seule la forme la plus réduite restera en tant qu'opcodes. par exemple: les vérifications liées, seront évaluées au moment de la construction, puisque leur chemin de code est const. Mais la valeur retournée sera * (données + offset)
v.oddou
16

constexprn'implique pas inlinepour les variables non statiques (variables en ligne C ++ 17)

Bien que constexprcela implique inlinepour les fonctions, cela n'a pas cet effet pour les variables non statiques, compte tenu des variables en ligne C ++ 17.

Par exemple, si vous prenez l'exemple minimal que j'ai publié sur: Comment fonctionnent les variables en ligne? et supprimez le inline, en laissant juste constexpr, alors la variable obtient plusieurs adresses, ce qui est la principale chose que les variables en ligne évitent.

constexpr les variables statiques sont cependant implicitement statiques.

Exemple minimal qui constexprimplique inlinepour les fonctions

Comme mentionné à l' adresse : https://stackoverflow.com/a/14391320/895245, l'effet principal de inlinen'est pas d'insérer en ligne mais d'autoriser plusieurs définitions d'une fonction, citation standard à l' adresse : Comment un fichier d'en-tête C ++ peut-il inclure une implémentation?

On peut l'observer en jouant avec l'exemple suivant:

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();
}

Compilez et exécutez:

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

Si nous supprimons inlinede shared_func, le lien échouerait avec:

multiple definition of `shared_func()'

car l'en-tête est inclus dans plusieurs .cppfichiers.

Mais si nous remplaçons inlinepar constexpr, cela fonctionne à nouveau, car cela constexprimplique aussi inline.

GCC implémente cela en marquant les symboles comme faibles sur les fichiers objets ELF: Comment un fichier d'en-tête C ++ peut-il inclure une implémentation?

Testé dans GCC 8.3.0.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
3
BTW, une variable de membre de classe statique déclarée constexprest toujours en ligne. cppreference.com : une variable membre statique (mais pas une variable de portée d'espace de noms) déclarée constexprest implicitement une variable en ligne.
anton_rh
@anton_rh merci, je n'avais pas vu cette règle, mettez à jour la réponse.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
ce n'est pas ce que dit open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf . il dit que constexpr implique en ligne pour les variables. sans mention d'une différence entre la portée de l'espace de noms de la portée de la classe.
v.oddou le