Fonctions Constexpr non appelées au moment de la compilation si le résultat est ignoré
J'étudiais des résultats de couverture de code plutôt étranges des constexpr
fonctions (les appels au moment de la compilation ne peuvent pas être instrumentés par l'outil de couverture de code que j'utilise), et j'ai remarqué que certaines constexpr
fonctions étaient appelées en tant que fonctions d' exécution , si les résultats de la fonction les appels n’ont pas été enregistrés .
Il semble que, pour les constexpr
fonctions ou les méthodes, si vous stockez les résultats de l'appel (soit dans une variable d'exécution [accentuation !!!], soit dans une constexpr
variable), l'appel est un appel à la compilation (tant que les paramètres sont compilés -temps). Si vous ignorez les résultats, l'appel est un appel d'exécution. Cela n'a rien à voir avec mon outil de couverture de code; le comportement semble être reproductible dans l'exemple simple ci-dessous.
Vous pourriez soutenir que puisque les constexpr
fonctions ne peuvent pas avoir d'effets secondaires, peu importe ce que fait le compilateur si vous ne retournez / n'utilisez pas de résultat. Je pense que pour l'efficacité, le compilateur ferait toujours ce qu'il peut être constexpr
, mais ce n'est ni ici ni là ... Ce que je me demande, c'est si c'est même un comportement défini.
Est-ce un moyen portable de garantir que vos constexpr
fonctions seront appelées en tant que runtime ??? Il n'y a pas une tonne d'utilisations pour cela, mais une utilisation est: si vous voulez "prendre le crédit pour les tests qui ont été appelés sur des constexpr
fonctions" dans la couverture de code, en appelant simplement la même fonction à la fin de votre test unitaire, et ignorant le résultat, afin qu'ils soient instrumentés.
Il existe d'autres moyens de forcer une fonction à être appelée en tant que runtime, je sais, je suis surtout curieux de savoir ce qui se passe ici. C'était très inattendu quand je l'ai vu pour la première fois. À moins que ce ne soit portable, j'appellerai probablement mes constexpr
méthodes via un objet d'exécution (ce qui semble faire l'affaire même pour les méthodes statiques), ou via un pointeur de fonction d'exécution.
Voir l'exemple ci-dessous. Démo en direct:https://onlinegdb.com/rJao0RNGP
// Modified from https://stackoverflow.com/a/40410624/12854372
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
Réponses
Cela peut être déroutant, mais les fonctions constexpr ne doivent être appelées au moment de la compilation que dans les contextes constexpr (affectation à la variable constexpr, utilisation pour la taille du tableau ou le paramètre de modèle, ...).
Dans un contexte normal, les fonctions sont appelées au moment de l'exécution. L'optimiseur peut résoudre cette fonction au moment de la compilation (comme pour toute autre fonction suivant la règle as-if). constexpr
est en effet un bon indice pour que l'optimisation se produise.
Vous pourriez soutenir que puisque les fonctions constexpr ne peuvent pas avoir d'effets secondaires
Ils peuvent avoir des effets secondaires, voir l'exemple valide suivant:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}
Démo