I have a class definition with 3 template parameters. I would like to create specializations of this class with specific combinations of 2 of the parameters, leaving the third free. How can I do that without duplicating code?
template < typename A, typename B, class C> Foo{}; // only defining particular specializations
template<class C> Foo<int, float, C>
{
int V1;
float V2;
friend C;
/* implementation using V1, V2 */
}
template<class C> Foo<bool, char, C>
{
bool V1;
char V2;
friend C;
/* duplicate implementation using V1, V2 */
}
main {
Foo<bool, char someclassname> fbc;
Foo<int, float, otherclassname> fif;
}
CodePudding user response:
You can use concept
// or simply drop this if you don't need it
template <typename A, typename B, class C>
struct Foo{};
template<typename A, typename B, class C>
requires std::same_as<A,int> && std::same_as<B,float>
|| std::same_as<A,bool> && std::same_as<B,char>
struct Foo<A, B, C>
{
A V1;
B V2;
friend C;
/* implementation using V1, V2 */
};
or inherit from a common base class
template <typename A, typename B, class C>
struct Foo{};
template <typename A, typename B, class C>
struct Foo_impl
{
A V1;
B V2;
friend C;
/* implementation using V1, V2 */
};
template<class C>
struct Foo<int,float,C>:Foo_impl<int,float,C>{};
template<class C>
struct Foo<bool,char,C>:Foo_impl<int,float,C>{};
if you don't need the default case, you can also static_assert
it
template <typename A, typename B, class C>
struct Foo{
static_assert(std::is_same_v<A,bool> && std::is_same_v<B,char>
|| std::is_same_v<A,int> && std::is_same_v<B,float>);
A V1 = 10;
B V2;
friend C;
/* implementation using V1, V2 */
};
CodePudding user response:
My original question above was not detailed enough and did not compile. I found the solution as a combination of the advices above. Thanks to all the responders. Here I post the full example (compiles with C 20)
#include <type_traits>
#include <concepts>
enum class TypeID: int { INTEGER=1, FLOAT};
// abstract base class, defines interface for a RTTI mechanism
class Base
{
public:
Base() {};
virtual ~Base() {};
virtual TypeID GetID() = 0; // RTTI
};
template< typename V, TypeID ID, class F>
class Numeric : public Base
{
static_assert(
(std::same_as<V, int> && TypeID::INTEGER ==ID) ||
(std::same_as<V, float> && TypeID::FLOAT == ID)
, "Error message here");
friend F;
public:
Numeric(V argValue=0) { V1 = argValue; };
virtual ~Numeric(){};
TypeID GetID() { return ID; }
V GetValue() { return V1; }
protected:
V V1=0;
void SetValue(V argValue) { V1 = argValue; }
};
template <class F> using NumericInt = Numeric<int, TypeID::INTEGER, F>;
template <class F> using NumericFloat= Numeric<float, TypeID::FLOAT, F> ;
class FriendlyClass {
public:
FriendlyClass(int argInitial) { mN.SetValue(argInitial); };
private:
NumericInt<FriendlyClass> mN= NumericInt<FriendlyClass>();
};
int main(){
NumericInt<FriendlyClass> N1(1); // OK
NumericFloat<FriendlyClass> N2(2); // OK
Numeric<int, TypeID::INTEGER, FriendlyClass> N3(3); // same as alias NumericInt , but OK
Numeric<int, TypeID::FLOAT, FriendlyClass> N4(4); // NOT ok, will static_assert()
return 0;
};