I am working on a project that involves some static polymorphism via template metaprogramming. The classes interact via mathematic operations that should recognize both scalar and class object arguments. A basic example of the polymorphism and object-object interaction is:
#include <iostream>
template<typename CLASS_T>
class BaseClass {};
template<class DATA_T>
class DerivedType1 : public BaseClass< DerivedType1<DATA_T> > {};
template<class DATA_T>
class DerivedType2 : public BaseClass< DerivedType2<DATA_T> > {};
// Operator template for DerivedType - DerivedType multiplications
template<typename CLASS_T1, typename CLASS_T2>
int operator* (BaseClass<CLASS_T1> const& obj1, BaseClass<CLASS_T2> const& obj2)
{
// Multiplication operation
return 0; // would actually return an expression template, though this isn't relevant to the current issue
}
// Main script
int main()
{
DerivedType1<float> obj1;
DerivedType2<float> obj2;
int val = obj1*obj2;
std::cout << val << std::endl;
return 0;
}
This seems to work perfectly well until I attempt to add a template specialization to account for multiplication with a scalar.
template<typename CLASS_T, class T>
int operator* (BaseClass<CLASS_T> const& obj, T scalar)
{
// Multiplication operation
return 1;
}
It seems that this new template specialization is always preferred over the former, even for the obj1*obj2
interaction, causing the compilation to fail. The scalars must be passed by value to account for literals however the class objects must be passed by reference as they may contain large arrays of data that should not be copied.
What ways could I achieve the desired outcome?
It seems that some kind of template specialization (perhaps using type_traits
and is_scalar
) could offer a solution though I am not entirely sure how to execute on this for functions and operators. Other suggestions or insight on template deduction are welcome as well.
CodePudding user response:
In C 20 you can put a requirement on your template parameters, like this:
template<typename T>
concept Scalar = std::is_scalar_v<T>;
template<typename CLASS_T, Scalar T>
int operator* (BaseClass<CLASS_T> const& obj, T scalar) { ... }
Alternatively (if you need it in older version of C ) you can implement similar logic with use of std::enable_if_t
template:
template<typename T>
using enable_if_scalar = std::enable_if_t<std::is_scalar_v<T>, bool>;
template<typename CLASS_T, typename T, enable_if_scalar<T> = true>
int operator* (BaseClass<CLASS_T> const& obj, T scalar) { ... }