J'ai du mal à comprendre pourquoi gcc-8.2.0 et clang-7.0.0 rejettent le code suivant (code live ici ):
#include <array>
int main() {
constexpr std::array<int,3> v{1,2,3};
constexpr auto b = v.begin(); // error: not a constexpr
return 0;
}
avec l'erreur
error: '(std::array<int, 3>::const_pointer)(& v.std::array<int,3>::_M_elems)'
is not a constant expression (constexpr auto b = v.begin();)
Selon en.cppreference.com , la fonction membre begin()
est déclarée constexpr
. S'agit-il d'un bug du compilateur?
Alors, évitons std::array
pour vous faciliter la tâche:
template <typename T, size_t N>
struct array {
T elems[N];
constexpr T const* begin() const { return elems; }
};
void foo() {
constexpr array<int,3> v{{1, 2, 3}};
constexpr auto b = v.begin(); // error
}
constexpr array<int, 3> global_v{{1, 2, 3}};
constexpr auto global_b = global_v.begin(); // ok
Pourquoi b
est-il une erreur mais global_b
est correct? De même, pourquoi b
deviendrait-il correct si nous déclarions que v
était static constexpr
? Le problème concerne essentiellement les pointeurs. Pour avoir une expression constante qui est un pointeur, elle doit toujours pointer vers une chose connue et constante. Cela ne fonctionne pas vraiment pour les variables locales sans durée de stockage statique, car elles ont une adresse fondamentalement mutable. Mais pour les statiques fonctionnelles locales ou les globales, elles ont une adresse constante, vous pouvez donc prendre un pointeur constant vers elles.
En standard, de [expr.const]/6 :
A expression constante est soit une expression constante de noyau glvalue qui fait référence à une entité qui est le résultat autorisé d'une expression constante (telle que définie ci-dessous), soit une expression constante de noyau prvalue dont la valeur satisfait les éléments suivants contraintes:
- si la valeur est un objet de type classe, [...]
- si la valeur est de type pointeur, elle contient l'adresse d'un objet avec une durée de stockage statique , l'adresse après la fin d'un tel objet ([expr .add]), l'adresse d'une fonction ou une valeur de pointeur nulle, et
- [...]
b
ne fait pas partie de ces choses dans la deuxième puce, donc cela échoue. Mais global_b
satisfait la condition en gras - comme le ferait b
si v
était déclaré static
.