Je suis en train d'essayer de comprendre si je peux utiliser des concepts comme une sorte d'interface pour les classes sans nécessiter le surdébit d'une table virtuelle. J'ai mis en place un exemple qui fonctionne plus ou moins, mais je dois stocker les instances de ma classe dans un tableau défini par leur héritage commun plutôt que leur concept commun. Je ne vois rien de discuté dans les messages à propos des tableaux de concepts, mais g++ 6.3.0 ne semble pas le permettre. L'erreur est :
$ g++ -fconcepts -std=c++1z custom_concept.cpp
custom_concept.cpp: In function ‘int main()’:
custom_concept.cpp:37:20: error: ‘shapes’ déclaré comme tableau de ‘IShape*’
IShape* shapes[2] = {&square, &rect}; // ne fonctionne pas
^
custom_concept.cpp:39:25: error: ‘shapes’ n'a pas été déclaré dans cette portée
for (IShape* shape : shapes )
^~~~~~
Si je change le tableau de IShape*
en un tableau de Rectangle*
(comme dans la ligne de commentaire en dessous de celle qui a causé la première erreur), le programme compile et s'exécute comme prévu.
Pourquoi le tableau de pointeurs de concept n'est-il pas autorisé ? Est-ce probable que cela soit autorisé dans une version future de c++ ?
(Mon exemple inclut des fonctions virtuelles et de l'héritage, même si mon but était de les éliminer. Je les ai inclus seulement pour faciliter le fonctionnement de la version Rectangle*
. Si je réussis à faire fonctionner la version IShape*
, je prévois de supprimer les fonctions virtuelles et l'héritage.)
Voici le code :
#include
template
concept bool IShape = requires (T x, T z, int y)
{
{ T() } ;
{ T(x) } ;
{ x = z } -> T& ;
{ x.countSides() } -> int ;
{ x.sideLength(y) } -> int ;
};
struct Rectangle
{
Rectangle() {};
Rectangle(const Rectangle& other) {};
Rectangle& operator=(Rectangle& other) {return *this; };
virtual std::string getName() { return "Rectangle"; }
int countSides() {return 4;}
virtual int sideLength(int side) { return (side % 2 == 0) ? 10 : 5; }
};
struct Square : public Rectangle
{
Square() {};
Square(const Square& other) {};
Square& operator=(Square& other) {return *this; };
std::string getName() override { return "Square"; }
int sideLength(int side) override { return 10; }
};
int main()
{
Square square;
Rectangle rect;
IShape* shapes[2] = {&square, &rect}; // ne fonctionne pas
// Rectangle* shapes[2] = {&square, &rect}; // fonctionne
for (IShape* shape : shapes )
{
for (int side = 0 ; side < shape->countSides() ; ++side )
{
std::cout << shape->getName() << " side=" << shape->sideLength(side) << "\n";
}
}
return 0;
};
Merci à @Yakk pour l'idée d'utiliser le tuple. G++ 6.3.0 n'avait pas pleinement mis en œuvre le fichier include pour inclure apply() tel que le standard C++17 le définit, mais il était disponible dans std::experimental. (Je pense qu'il peut être ajouté à dans une version ultérieure de g++.) Voici ce avec quoi j'ai fini :
#include
#include
#include
template
concept bool IShape = requires (T x, T z, int y)
{
{ T() } ;
{ x = z } -> T& ;
{ T(x) } ;
{ x.countSides() } -> int ;
{ x.sideLength(y) } -> int ;
};
struct Rectangle
{
Rectangle() {};
Rectangle(const Rectangle& other) {};
Rectangle& operator=(Rectangle& other) {return *this; };
std::string getName() { return "Rectangle"; }
int countSides() {return 4;}
int sideLength(int side) { return (side % 2 == 0) ? 10 : 5; }
};
struct Square
{
Square() {};
Square(const Square& other) {};
Square& operator=(Square& other) {return *this; };
std::string getName() { return "Square"; }
int countSides() {return 4;}
int sideLength(int side) { return 10; }
};
void print(IShape& shape)
{
for (int side = 0 ; side < shape.countSides() ; ++side )
{
std::cout << shape.getName() << " side=" << shape.sideLength(side) << "\n";
}
};
int main()
{
Square square;
Rectangle rect;
auto shapes = std::make_tuple(square, rect);
std::experimental::apply([](auto&... shape) { ((print(shape)), ...); }, shapes) ;
return 0;
};