15

I have been trying to choose between two templated functions based on whether an overload operator<<(std::ostream&, const T&) exists.

Example:

template <typename T, typename std::enable_if</* ? */, int>::type = 0> std::string stringify(const T& t) { std::stringstream ss; ss << t; return ss.str(); } template <typename T, typename std::enable_if</* ? */, int>::type = 0> std::string stringify(const T& t) { return "No overload of operator<<"; } struct Foo { }; int main() { std::cout << stringify(11) << std::endl; std::cout << stringify(Foo{}) << std::endl; } 

Is this possible? And if so, how would you solve this problem?

1
  • The question has been marked as duplicate to a non-C++11 question. But a C++11 answer to that question can be found here among its answers: stackoverflow.com/a/9154394/1728537 Commented Jun 3, 2014 at 13:45

1 Answer 1

21

There's no need for enable_if, use expression SFINAE to select the correct overload when the operator<< is present.

namespace detail { template<typename T> auto stringify(std::stringstream& ss, T const& t, bool) -> decltype(ss << t, void(), std::string{}) { ss << t; return ss.str(); } template<typename T> auto stringify(std::stringstream&, T const&, int) -> std::string { return "No overload of operator<<"; } } template <typename T> std::string stringify(const T& t) { std::stringstream ss; return detail::stringify(ss, t, true); } 

Live demo

The stringify function template simply delegates to one of the detail::stringify function templates. Then, the first one is selected if the expression ss << t is well-formed. The unnamed bool parameter is being used for disambiguation between the two detail::stringify implementations. Since the primary stringify function passes true as the argument to detail::stringify, the first one will be a better match when the operator<< overload is present. Otherwise the second one will be selected.

This expression decltype(ss << t, void(), std::string{}) in the trailing return type of the first stringify template probably merits a more detailed explanation. Here we have a single expression consisting of 3 sub-expressions separated by the comma operator.

The first one, ss << t is what determines whether that function template passes template parameter substitution and will be added to the overload resolution set. This will occur if the expression is well-formed, i.e. if the type in question overloads operator<<.

The middle sub-expression, void() doesn't do anything other than ensure that some user-defined operator, is not selected (because you cannot overload operator, with a void parameter type).

The third, and rightmost, sub-expression, std::string{} is what determines the return type of the detail::stringify function.

Sign up to request clarification or add additional context in comments.

15 Comments

Not to go too off topic, but I don't understand the decltype in your first function. Why does it have two arguments? Isn't it just going to be std::string?
@Adam: It doesn't have two arguments; it has a single argument, consisting of an expression with a comma operator. If ss << t is well-formed, then the type of the full expression is std::string. If it isn't, then substitution fails and the template isn't instantiated, so the second version is selected instead.
So does the ss << t expression have to appear in the decltype for SFINAE to apply? Wouldn't the ss << t in the body also serve the same purpose?
@Adam -- Substitution occurs in the types/expressions used in the function type (that is, its return type and parameter types) and its template parameters. So while ss << t doesn't necessarily need to occur in the decltype, the ss << t in the body won't serve the same purpose because then the substitution would have succeeded and would lead to a hard compilation error rather than the function being SFINAE'd out of the overload set.
You might aswell have just linked to my answer, where I also explain all that. :P /cc @Yakk
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.