Home > database >  Match a template pattern only if another pattern is matched
Match a template pattern only if another pattern is matched

Time:01-25

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
  // ...
}

Demo

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*/ }
  • Related