Reputation: 310
I have the defined the following types:
template <typename TType>
struct symbol{};
template <typename TAtom, unsigned IDim>
struct tensor_type {};
template <unsigned IDim>
using real = tensor_type<double, IDim>;
template <unsigned IDim>
using index = tensor_type<int, IDim>;
I now want to call functions with instances of the type symbol like this:
template <template<typename TType, unsigned IDim> typename TTemplate, typename TType, unsigned IDim>
void f(symbol<TTemplate<TType, IDim>>* sym) {
std::cout << "higher dimension" << std::endl;
}
int main() {
symbol<real<0>>* p1 = new symbol<real<0>>();
symbol<index<0>>* p2 = new symbol<index<0>>();
symbol<real<1>>* p3 = new symbol<real<1>>();
symbol<index<5>>* p4 = new symbol<index<5>>();
f(p1); //dimension 0
f(p2); //dimension 0
f(p3); //higher dimension
f(p4); //higher dimension
}
This works fine and well. But now I want to differ between symbols of the dimension 0 and all the other dimensions, but I do not know how to properly declare an overloaded function properly with the templates to achive this.
I have tried the following:
template <typename TType>
void f(symbol<TType<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: ‘TType’ is not a template
during compilation and
template <template<typename TType> typename TTemplate, typename TType>
void f(symbol<TTemplate<TType>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: wrong number of template arguments (2, should be 1)
.
I cannot include the code for both dimensions in one function because in the actual implementation types with dimension 0 have different attributes than the one with higher dimensions, so even if I use a case distinction in the function the compiler complains that some passed arguments do not have the needed attribute.
One possiblity is to declare a function for every possible type for dimension 0 like this
void f(symbol<real<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
but since I have multiple types in the actual implementation and all the functions would look exactly the same (besides the signature) I wanted to avoid redundant code.
Is there some way to achive this more elegant?
Upvotes: 0
Views: 103
Reputation: 76829
In your shown example, there is only one relevant class template: tensor_type
The others declared with using
are just aliases for this one. They do not define new class templates or types.
So with that it is enough to simply specify that template in the overload:
template <typename T>
void f(parameter_symbol<tensor_type<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
If you have multiple templates, some of which you haven't shown, then
template <template<typename, unsigned> typename TTemplate, typename T>
void f(parameter_symbol<TTemplate<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
will work. Note that the kinds of template parameter in the template template parameter template<typename, unsigned> typename TTemplate
must match the class template that you are going to use as argument. (As one exception, you can also replace unsigned
with auto
.)
If you have multiple templates with different kinds of template parameters, then you need define such overloads for all cases. If the dimension is not always in the same position in the parameter list, then this approach doesn't work.
It would be simpler to have the relevant classes provide a constexpr
static member variable idim
which indicates the dimension, or a constexpr
static function returning it. Or alternatively a specialized free variable template or function template.
Then you can have a function overload taking any type and constrain it through SFINAE or if you can use C++20, a requires
class:
template <typename T>
requires(T::idim == 0)
void f(parameter_symbol<T>* sym) {
std::cout << "dimension 0" << std::endl;
}
Upvotes: 2