Comportement incorrect du compilateur d'arguments lambda C ++

Oct 16 2020

Lesquelles des instructions lambdas / instructions C ++ suivantes sont censées fonctionner selon la dernière spécification C ++?

Contexte au cas où cela serait pertinent: voir ici .

J'ai testé les extraits de code suivants -std=c++17sur Fedora 33 avec clang 11.0.0 et gcc 10.2.1.

Mise à jour: remplacez __PRETTY_FUNCTION__par __func__pour une conformité standard. Le même comportement peut être observé.

Update2: Exemple d' utilisation const char * s = __func__comme argument par défaut pour vérifier qu'il doit être valide dans une portée de fonction (grâce à @BenVoigt).


1. LLVM __func__dans l'argument par défaut lambda

void clang() {
  [](const char* c = __func__) {std::cout << c << std::endl;}();
}

Comportement attendu (CLANG):

  • Imprimer clang\n( void clang()pour __PRETTY_FUNCTION__)

Comportement observé (CLANG):

  • Avertissement du compilateur: warning: predefined identifier is only valid inside function [-Wpredefined-identifier-outside-function]
  • Imprimer \n( top level()pour __PRETTY_FUNCTION__)

2. GCC ignore les déclarations

template <typename L>
constexpr std::string_view methodName(L l) { return l(); }
#define __METHOD_NAME__ (\
  __func__, /* needed for pointer to work */ \
  methodName([](const char* c = __func__) {return std::string_view(c);}) \
)
void gcc1() {
  std::cout << [](const char* c = __func__) { return c; }() << std::endl;  // GCC: This statement doesn't do anything
  std::cout << [](const char* c = __func__) { return c; }("gcc") << std::endl;
  std::cout << __METHOD_NAME__ << std::endl;  // GCC: This statement somehow conflicts with the statements above
}
void gcc2() {
  std::cout << __METHOD_NAME__ << std::endl;  // GCC: This statement itself works
}

Sortie attendue (GCC):

gcc1
gcc
gcc1
gcc2

Sortie observée (GCC):

gcc
gcc2

3. Erreur de compilation GCC

void gcc3() {
  std::string_view s = [](const char* c = __func__) { return std::string_view(c); }();
  std::cout << s << std::endl;
}

Comportement attendu (GCC): Compile sans problème.

Comportement observé (GCC): error: internal compiler error: in finish_expr_stmt

Réponses

n.'pronouns'm. Oct 16 2020 at 17:01

[class.local] La classe locale est dans la portée de la portée englobante, et a le même accès aux noms en dehors de la fonction que la fonction englobante. [Note: Une déclaration dans une classe locale ne peut pas utiliser odr (6.2) une entité locale à partir d'une portée englobante. - note de fin]

Un lambda est une classe locale, et en tant que tel, il ne peut pas utiliser de variables de la portée englobante (par exemple __func__) autre que dans sa clause de capture.