Подтвердить что ты не робот

Использование SFINAE для выбора функции в зависимости от того, существует ли определенная перегрузка функции

Я пытаюсь выбрать между двумя шаблонами, основанными на том, существует ли перегрузка operator<<(std::ostream&, const T&).

Пример:

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;
}

Возможно ли это? И если да, как бы вы решили эту проблему?

4b9b3361

Ответ 1

Нет необходимости в enable_if, используйте выражение SFINAE для выбора правильной перегрузки, когда присутствует operator<<.

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);
}

Живая демонстрация

Шаблон функции stringify просто делегирует один из шаблонов функций detail::stringify. Затем первый выбирается, если выражение ss << t хорошо сформировано. Неименованный параметр bool используется для устранения различий между двумя реализациями detail::stringify. Поскольку основная функция stringify проходит true как аргумент detail::stringify, первая будет лучше соответствовать, когда присутствует перегрузка operator<<. В противном случае будет выбран второй.

Это выражение decltype(ss << t, void(), std::string{}) в обратном типе возврата первого шаблона stringify, вероятно, заслуживает более подробного объяснения. Здесь мы имеем одно выражение, состоящее из 3 подвыражений, разделенных оператором запятой.

Первый, ss << t то, что определяет, проходит ли, что шаблон функции подстановки параметров шаблона и будет добавлен к набору разрешения перегрузки. Это произойдет, если выражение корректно сформировано, т.е. Если рассматриваемый тип перегружает operator<<.

Средняя Подвыражение, void() не что-либо делать, кроме того, чтобы некоторые определенные пользователем operator, не выбран (потому что вы не можете перегрузить operator, с void типа параметра).

третий, и крайний правый, суб-выражение, std::string{} это то, что определяет тип возвращаемого detail::stringify функции.