I'm reading the source code of the main json11 header file.
It contains the following declaration:
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
I'm trying to find some documentation about this usage of decltype
and class
inside a template declaration but no success.
Does this construction/usage has a name in C++? Any good reference about it?
It's using SFINAE ("Substitution Failure Is Not An Error"), a common technique for advanced template stuff. In this case, it's used as a crude(1) test whether the type T
has a function named to_json
.
How it works: if the expression T::to_json
is well-formed (there is something named to_json
inside the type T
), decltype(T::to_json)
denotes a valid type and the constructor template can be used normally.
However, if T::to_json
is ill-formed (i.e. if there is no to_json
member inside T
), it means substituting the template argument for T
has failed. Per SFINAE, this is not an error of the entire program; it just means that the template is removed from further consideration (as if it was never part of the class).
The effect is thus that if type T
has a member to_json
, you can use an object of type T
to initialise a Json
object. If there is no such member in T
, the constructor will not exist.
(1) I'm saying crude test because this only checks that T
has such a member. It doesn't check that the member is a function which can be invoked without arguments and returns something another constructor of Json
can accept. A tighter-fitting test might look something like this:
template <class T, class = std::enable_if_t<std::is_constructible<Json, decltype(std::declval<const T>().to_json())>::value>>
Json(const T & t) : Json(t.to_json()) {}