Jeremy Sigrist
Jeremy Sigrist

Reputation: 325

Can a recursive variadic template function be implemented inside a template class?

I have a std::any that contains a numeric type, and I'm trying to cast the value in the any to a specific type via a recursive variadic template helper class

#include <any>
#include <cstdint>
#include <iostream>

template <typename ResultType>
struct Cast_any_to
{

template<>
static ResultType from_one_of(std::any const& any_)
{
  throw std::bad_any_cast();
}

template <typename T, typename... Ts>
static ResultType from_one_of(std::any const& any_)
{
  if (any_.type() == typeid(T))
  {
    return static_cast<ResultType>(std::any_cast<T>(any_));
  }

  return from_one_of<Ts...>(any_);
}
};

// Usage example
int main()
{
  std::any val{1.2F};
  int32_t result = Cast_any_to<int32_t>::from_one_of<int32_t, uint32_t, float, double>(val);
  std::cout << result << "\n";
  return 0;
}

This won't compile; clang gives the error message:

cast_any_to.cpp:10:19: error: no function template matches function template specialization 'from_one_of'
static ResultType from_one_of(std::any const& any_)
                  ^
cast_any_to.cpp:31:20: note: in instantiation of template class 'Cast_any_to<int>' requested here
  int32_t result = Cast_any_to<int32_t>::from_one_of<int32_t, uint32_t, float, double>(val);

If I remove the function from the class and add ResultType to the template list it compiles, but this hurts readability at the call site (IMO). Is there a way to make this work?

Upvotes: 1

Views: 54

Answers (1)

Vittorio Romeo
Vittorio Romeo

Reputation: 93274

You cannot explicitly specialize a template unless you are in namespace scope. If you want to keep everything in the class definition, use function overloading instead:

template <typename... Ts>
struct TypeList {};

template <typename ResultType>
class Cast_any_to {
 private:
  static ResultType from_one_of_impl(TypeList<>, std::any const&) {
    throw std::bad_any_cast();
  }

  template <typename T, typename... Ts>
  static ResultType from_one_of_impl(TypeList<T, Ts...>, std::any const& any_) {
    if (any_.type() == typeid(T)) {
      return static_cast<ResultType>(std::any_cast<T>(any_));
    }

    return from_one_of_impl(TypeList<Ts...>{}, any_);
  }

 public:
  template <typename T, typename... Ts>
  static ResultType from_one_of(std::any const& any_) {
    return from_one_of_impl(TypeList<T, Ts...>{}, any_);
  }
};

Otherwise, move the templates to an implementation namespace:

namespace impl {

template <typename ResultType>
static ResultType from_one_of(std::any const&) {
  throw std::bad_any_cast();
}

template <typename ResultType, typename T, typename... Ts>
static ResultType from_one_of(std::any const& any_) {
  if (any_.type() == typeid(T)) {
    return static_cast<ResultType>(std::any_cast<T>(any_));
  }

  return from_one_of<Ts...>(any_);
}

}  // namespace impl

template <typename ResultType>
struct Cast_any_to {
  template <typename T, typename... Ts>
  static ResultType from_one_of(std::any const& any_) {
    return impl::from_one_of<ResultType, T, Ts...>(any_);
  }
};

Upvotes: 3

Related Questions