Reputation: 325
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
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