I'm trying to design a class - let's call it A for the sake of discussion - that will fulfill a specific set of requirements:
Amust be a literal type to allow the compiler to initialize its global instances at compile time viaconstexprconstructors (many globalconstobjects of this type are created in the source code).- Its central private field is a simple integer. However, the class has two constructors:
A::A(int x)andA::A(int x, int y, int z). If the first version is invoked, then later, at runtime, that singlexwill be used by the class internally whenever a method call that needs to use it is made. In contrast, if the second version with three parameters is used, it will be decided at runtime which one ofx,yandzto use if a function that needs one is invoked. Amust be a single type; one type for the first constructor and another type for the second constructor is not satisfactory (Ais frequently passed to functions as an argument, so without this constraint, I would need to duplicate or templatize all those functions).- A great majority of all
Aobjects will be global constants, assignment will seldom happens, and if it does, it will certainly not be between an object with threeints and an object with oneintor vice-versa.
The solutions I have considered so far are as follows:
-
Make
Aa template; the template parameter would be what I called theStorageType. That storage would abstract access to that centralintresource by exposing an accessor for it. The problem of choosing whichintfield to use would then be moved from theAto this helper storage type. The idea is roughly illustrated by this code:template<typename StorageType> class A { private: StorageType storage; public: constexpr A(int x, int y, int z) : storage(x, y, z) { } constexpr A(int x) : storage(x) { } void doSomething() { auto theRightInt = storage.getInt(); // ... } };Problem: violates constraint 3.
-
As before, but rather than templatize
Aon theStorageType, have a generic interface that describes theStorageTypeand store aunique_ptrto one insideA.Problem: violates constraint 1.
-
Store the integers as a union:
union { struct { int x; int y; int z; } intPack; int singleInt; };In this variant, each
Aobject - including those that only use a singleint- has room for three possibleints. Another option would be to useboost::variantinstead of the obsoleteunion.Problem: This is wasteful - see point 4. If
boost::variantis used, it violates constraint 1 sinceboost::variant, unlikestd::variantfrom C++17, is not a literal type. -
Rather than attempt to represent the "variance" of the central integer field inside
A, do it externally: have the class always store a singleint, and create a helper class - let's call itVariantA- that contains three versions ofA, each initialized with what wasx,yandzin the constructor mentioned in point 2.VariantAwould have an accessor function that decides at runtime whichAobject to return.Problem: Tedious to use because the accessor has to be invoked every single time:
VariantA a1(0, 1, 2); VariantA a2(10, 20, 30); auto a3 = a1.get() + a2.get(); someFunctionThatTakesAnA(a1.get()); // etc
Question: Is there an elegant solution to this problem that I have missed, or will I have to choose one of the solutions I have considered (and rejected) above? My platform is VC++, so use of C++11/4 is okay except some more esoteric parts, but the features from C++17 drafts are not yet available.
Aucun commentaire:
Enregistrer un commentaire