I am trying to provide out-of-class definitions of arithmetic operators +-*/
(and in-place +=
etc.) for differently templated types. I read that C++20 concepts is the good way to go, as one could constrain the input/output type to provide only one templated definition, although I could not find much examples of this...
I am using a type-safe vector as base class:
// vect.cpp
template<size_t n, typename T>
struct Vect {
Vect(function<T(size_t)> f) {
for (size_t i=0; i < n; i++) {
values[i] = f(i);
}
}
T values [n];
T operator[] (size_t i) {
return values[i];
}
}
I have a derived class for tensors like so:
// tensor.cpp
template <typename shape, typename T>
struct Tensor : public Vect<shape::size, T> {
// ... same initiliazer and [](size_t i)
}
and I shall also define a derived class for read-only views/slices, overriding operator []
to jump accross strides. I'd like to hard code little more than fmap
and fold
methods inside each class and avoid reproducing boilerplate code as much as possible.
I first had a bit of trouble coming up with a suitable concept for Vect<n,T>
-like classes due to different template parameters, but the one below seems to work:
// main.cpp
template<typename V, int n, typename T>
concept Vector = derived_from<V, Vect<n, T>>
template<int n, typename T, Vector<n, T> V>
V operator + (const V& lhs, const V& rhs) {
return V([&] (int i) {return lhs[i] + rhs[i];});
}
int main () {
size_t n = 10;
typedef double T;
Vect<n,T> u ([&] (size_t i) {return static_cast<T>(i) / static_cast<T>(n);});
log("u + u", u);
return 0;
}
Error: template deduction/substitution failed, could not deduce template parameter 'n'
Try 2:
Based on this question I figure out-of-class definition has to be a little more verbose, so I added a couple lines to vect.cpp
.
This seems contrived as it would require (3 * N_operators) type signature definitions, where avoiding code duplication is what motivates this question. Plus I don't really understand what the friend
keyword is doing here.
// vect.cpp
template<size_t n, typename T>
struct Vect;
template<size_t n, typename T>
Vect<n, T> operator + (const Vect<n, T>& lhs, const Vect<n, T>& rhs);
template<size_t n, typename T>
struct Vect {
...
friend Vect operator +<n, T> (const Vect<n, T>& lhs, const Vect<n, T>& rhs);
...
}
Error: undefined reference to Vect<10, double> operator+(Vect<10, double> const&, Vect<10, double> const&)' ... ld returned 1 exit status
I am guessing the compiler is complaining about implementation being defined in main.cpp
instead of vect.cpp
?
Question: What is the correct C++ way to do this? Are there any ways to make the compiler happy e.g. with header files?
I am really looking for DRY answers here, as I know the code would work with a fair amount of copy-paste :)
Thanks!