web-dev-qa-db-fra.com

Quand une fonction constexpr est-elle évaluée au moment de la compilation?

Puisqu'il est possible qu'une fonction déclarée comme constexpr puisse être appelée pendant l'exécution, selon quels critères le compilateur décide-t-il de la calculer au moment de la compilation ou pendant l'exécution?

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

int main(int argc, char** argv)
{
    int i = 0;
    std::cin >> i;

    std::cout << POW(i, 2) << std::endl;
    return 0;
}

Dans ce cas, i est inconnu au moment de la compilation, ce qui est probablement la raison pour laquelle le compilateur traite POW () comme une fonction régulière qui est appelée au moment de l'exécution. Cette dynamique cependant, aussi pratique qu'elle puisse paraître, a des implications peu pratiques. Par exemple, pourrait-il y avoir un cas où j'aimerais que le compilateur calcule une fonction constexpr pendant la compilation, où le compilateur décide de la traiter comme une fonction normale à la place, alors qu'il aurait également fonctionné pendant la compilation? Y a-t-il des pièges communs connus?

61
Byzantian

constexpr fonctions sera être évalué au moment de la compilation lorsque tous ses arguments sont des expressions constantes et que le résultat est également utilisé dans une expression constante. Une expression constante peut être un littéral (comme 42), un argument de modèle non type (comme N dans template<class T, size_t N> class array;), une déclaration d'élément enum (comme Blue dans enum Color { Red, Blue, Green };, une autre variable déclarée constexpr, et ainsi de suite.

Ils pourraient être évalués lorsque tous ses arguments sont des expressions constantes et que le résultat est pas utilisé dans une expression constante, mais cela dépend de l'implémentation.

82
K-ballo

La fonction doit être évaluée au moment de la compilation lorsqu'une expression constante est nécessaire.

La méthode la plus simple pour garantir cela consiste à utiliser une valeur constexpr ou std::integral_constant:

constexpr auto result = POW(i, 2); // this should not compile since i is not a constant expression
std::cout << result << std::endl;

ou:

std::cout << std::integral_constant<int, POW(i, 2)>::value << std::endl;

ou

#define POW_C(base, power) (std::integral_constant<decltype(POW((base), (power)), POW((base), (power))>::value)

std::cout << POW_C(63, 2) << std::endl;

ou

template<int base, int power>
struct POW_C {
  static constexpr int value = POW(base, power);
};

std::cout << POW_C<2, 63>::value << std::endl;
19
Pubby