简体   繁体   中英

c++ templates, how to map template parameters to other template parameters

Can I somehow make a map of template parameters? let's say I have the following function:

template<typename T>
T SumCoefficients(const std::vector<T>& coeffs) {
  T sum = static_cast<T>(0);
  for(int i=0; i<100; ++i) {
    sum += SomeFunc<T>(i) * coeffs[i];
  }
  return sum;
}

// explicit instantiation

template double SumCoefficients(const std::vector<double>& coeffs);
template float SumCoefficients(const std::vector<float>& coeffs);
template Vector3d SumCoefficients(const std::vector<Vector3d >& coeffs);

now, let's say I don't want to call SomeFunc<T>(i) but instead, if T==float, I want to use SomeFunc<float> , if T==double I want to use SomeFunc<double> , but if T==Vector3d I don't want to use SomeFunc<Vector3d> but instead SomeFunc<double>

now of course I could explicitely implement template <> Vector3d SumCoefficients(std::vector<Vector3d > coeffs) {... } and then make the explicit call to SomeFunc<double> , but I wonder if there is an elegant way that gives me the same result with only a single template implementation plus explicit instantiation.

You can use constexpr if (since C++17) to check the type. eg

template<typename T>
T SumCoefficients(const std::vector<T>& coeffs) {
  T sum = static_cast<T>(0);
  for(int i=0; i<100; ++i) {
    if constexpr (std::is_same_v<T, Vector3d>) // when T is Vector3d
      sum += SomeFunc<double>(i) * coeffs[i];
    else
      sum += SomeFunc<T>(i) * coeffs[i];
  }
  return sum;
}

The constexpr if approach is fine, but I'd like to add another solution which I think is preferable, if you have many calls to SomeFunc across your code base.

Another advantage of my solution is that it scales better if you have many types or you need to be able to add types later because the mapping logic is encapsulated in the template specialisations rather in the calling code.

I assume, what you want semantically is something like the scalar type of T :

template<typename T>
struct scalar_type {

    using type = T;
};

template<typename T>
using scalar_t = typename scalar_type<T>::type;

Now you can add sepcializations of this template for all types of vectors or matrices or whatever you need.

template<>
struct scalar_type<Vector3d> {

    using type = double;
};

Your calling code would look like this:

template<typename T>
auto SumCoefficients(const std::vector<T>& coeffs) {
  scalar_t<T> sum;
  for(int i=0; i<100; ++i) {
    sum += SomeFunc<scalar_t<T>>(i) * coeffs[i];
  }
  return sum;
}

If you're restricted to c++11, the call site could look like this:

template<typename T, typename Scalar = scalar_t<T>>
Scalar SumCoefficients(const std::vector<T>& coeffs) {
  Scalar sum;
  for(int i=0; i<100; ++i) {
    sum += SomeFunc<Scalar>(i) * coeffs[i];
  }
  return sum;
}

See full example here

You asked for a C++11 solution, which can't use if constexpr :

template<typename T>
T SumCoefficients(const std::vector<T>& coeffs) {
  using TTag = typename std::conditional<std::is_same<T, Vector3d>::value, double, T>::type;
  TTag sum = static_cast<TTag>(0);
  for(int i=0; i<100; ++i) {
    sum += SomeFunc<TTag>(i) * coeffs[i];
  }
  return sum;
}

(I made some assumptions on return type of SomeFunc and maybe something else, as your question didn't have full details, but I hope this will work for you)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM