I have a template function defined only for some types:
template<typename T, std::enable_if_t<std::is_pod_v<T>, bool> = true >
void serialize(const T & t) { /*serialize POD*/ }
template<>
void serialize(const std::string & t) { /*serialize string*/ }
//....
And I would like to add a std::vector specialization that matches only if T can be serialized
template<typename T /*, is_serializable<T> */>
void serialize(const std::vector<T> & t) { /*do something*/ }
How can I make this one matching only if T himself matches a serialize() method?
CodePudding user response:
With C 20 constraints, you might do something like:
template <typename T>
void serialize(const std::vector<T>& v)
requires requires (T inner) {serialize(inner);}
{
// serialize vector
// ...
}
CodePudding user response:
Pre-C 20 solution: Test if you can serialize a type:
template <typename T>
auto is_serializable(T) -> decltype(serialize(std::declval<T>()), std::true_type());
std::false_type is_serializable(...);
template <typename T>
bool constexpr is_serializable_t
= decltype(is_serializable(std::declval<T>()))::value;
With this you can apply the same pattern as you had for the POD types already:
template<typename T, std::enable_if_t<is_serializable_t<T>, bool> = true >
void serialize(std::vector<T> const& t)
{ /*do something*/ }
Demonstration on godbolt.
CodePudding user response:
You can use the same technique you used for the original serialize() function, using std::enable_if_t and std::is_pod_v. You can create a new trait (e.g. is_serializable) that checks if T is serializable by checking if there is a matching serialize() function for T. You can then use std::enable_if_t to enable the std::vector specialization only if T is serializable.
template<typename T>
using is_serializable = decltype(serialize(std::declval<T>()));
template<typename T, std::enable_if_t<std::is_pod_v<T>, bool> = true >
void serialize(const T & t) { /*serialize POD*/ }
template<>
void serialize(const std::string & t) { /*serialize string*/ }
template<typename T, std::enable_if_t<std::is_detected_v<is_serializable, T>, bool> = true>
void serialize(const std::vector<T> & t) { /*do something*/ }
It's worth noting that this is going to make the function serialize(const std::vector & t) only be defined if T is serializable. If T is not serializable, the function won't be defined and the compiler will look for another overload or will throw an error if none is found.
CodePudding user response:
SFINAE variant can be as simple as:
template<typename T, typename = decltype(serialize(std::declval<T>())) >
void serialize(std::vector<T> const& t)
{ /*do something*/ }