Search code examples
c++functiontemplatesc++14template-argument-deduction

c++ template argument deduction in function prototype. possible workaround in this case?


Primer

I have a Vector class that does some math. Among other things, it has a mean() function that calculates the mean of all values inside the vector. It uses a sum() function that i added here just for completeness.

Vector

template <std::size_t N, typename T>
class Vector{
  public:
    
    // ...

    T sum() const {
      T ret{};
      for (const auto &item : data) {
        ret += item;
      }
      return ret;
    }

    template <typename U = T>
    U mean() const {
      return U(sum()) / N;
    }

  private:
    std::array<T,N> data;
};

The mean() function itself has a template parameter so that a caller of this function has the option to specify the type he wants his result in. For example, if Vector is of type int, the mean could be subject to precision loss if it is casted to the type of the Vector, in this case int:

Vector<3,int> vec{2,3,5};
int mean1 = vec.mean();
int mean2 = vec.mean<int>(); // same as mean1
float mean3 = vec.mean<float>();

As expected, the values are

3
3
3.333333f

This is all good. No problem here.

Problem

The problem i have is when i want to achieve the same with a free function (outside the class). My first, naive implementation was this

template <std::size_t N, class T>
T mean(const Vector<N, T> &other) {
  return other.mean();
}

But when trying to calculate the float mean, i'll have to always call this free function specifying both N and T, the template parameters.

Vector<3,int> vec{2,3,5};
float mean = mean<3, float>(vec);
//                ^^^i want to get rid of this "3"

Question

I understand that template argument deduction does not work in function prototypes... And that there is likely no way of deducting the parameter N somehow without specifying it exactly. But maybe there is a completely unrelated workaround to remove the need of having to write out the N value when calling the free function that i don't see?


Solution

  • Thanks to @Jarod42 and @HTNW i was able to hack it into a single function.

    With class U = void and std::conditional it is possible to make the specification of U optional for the caller.

    template <class U = void, std::size_t N, typename T>
    auto mean(const Vector<N, T> &v) {
      return v.template mean<std::conditional_t<std::is_same_v<U, void>, T, U>>();
    }
    

    Example

    Vector<3,int> v{2,3,5};
    int mean1 = mean(v);          // 3
    int mean2 = mean<int>(v);     // 3
    float mean3 = mean<float>(v); // 3.333333f
    

    Demo