I'm trying to specialize a template to work with both primary types, templated types and specialised/aliased templated types. See the code example below.
It compiles but does not link.
How can I write a template specialization for zero()
that I can call as zero<myvec>()
and still be able to also call zero<double>
, etc.?
Reason is that in my application, I'm not given the size N
of myvec
, so I cannot write zero<vec_t, N>()
, however, I know that myvec
is a template alias as below and I know the structure of the template etc., just not the sizes:
#include <iostream>
#include <array>
#include <vector>
template<std::size_t N>
using vec_t = std::array<double, N>;
using v_t = std::vector<double>;
using v5_t = vec_t<5>;
// generic declaration
template<typename T> T zero();
// full specialization to double
template<> double zero() { std::cout << " -> Double\n"; return 0;}
// full specialization to v_t
template<> v_t zero() { std::cout << " -> vector<double>\n"; return v_t{}; };
// full specialization to v5_t
template<> v5_t zero() { std::cout << " -> vec_t<5>\n"; return v5_t{}; };
// attempt at partial specialization to vec_t<N>
template<template<typename T, std::size_t N> typename V, typename T, std::size_t N> V<T, N> zero() {
std::cout << " -> V<T, N>\n";
return V<T, N>{};
};
template<template<std::size_t N> typename V, std::size_t N> V<N> zero() {
std::cout << " -> V<N>\n";
return V<N>{};
};
int main() {
double z1 = zero<double>(); // works
v_t z2 = zero<v_t>(); // works
v5_t z3 = zero<v5_t>(); // works
const std::size_t N = 6;
vec_t<N> z4 = zero<std::array, double, N >(); // works, but requires full specs of vec_t
vec_t<N> z5 = zero<vec_t, N>(); // works, but requires N
using myvec = vec_t<6>;
myvec z6 = zero<myvec>(); // linker error ! but is what I'd like to write
return 0;
}
You may have a look at CompilerExplorer.
Thanks a lot for your help! (P.S. Solutions up to Cpp17 are fine)
Thanks again for the two excellent answers. I really like the variadic template solution ;-). Turns out there is a simpler, albeit admittedly less generic, solution to the problem stated without replacing functions with structs. Just add a definition to the generic declaration of zero(), i.e. change
template <typename T> T zero();
to
template <typename T> T zero() {return {};};
and the code compiles and runs just fine. I feel a little bit of explanation as to why I do this silly stuff is in order: In my application I mix objects of type Eigen::Matrix, Eigen::Tensor, etc of different sizes (realized through template aliasing) with primary scalar types. Unfortunately, the Eigen types do not come with standard zero-initialisation through {}
but rather have static setZero() functions. The above solution allows me to use the Eigen specific initialisation as a standard and then fully specialize to the scalar types.
I updated the CompilerExplorer