Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SFINAE to select function based on whether a particular overload of a function exists [duplicate]

Tags:

c++

c++11

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?

like image 335
Kyle Mayes Avatar asked Jun 02 '14 05:06

Kyle Mayes


1 Answers

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.

like image 62
Praetorian Avatar answered Nov 07 '22 23:11

Praetorian