Разрешение перегрузки с универсальными ссылками

У меня есть функция, которая может принимать любой тип по универсальной ссылке, и я хотел бы перегрузить его для определенных типов (некоторые из них сами по себе шаблонизированы, хотя я не думаю, что это важно здесь). К сожалению, я не могу понять, что перегрузки должны быть решены в правильном порядке.

Я бы предположил, что вторая декларация foo было бы предпочтительнее, так как он более конкретный (менее шаблонный), хотя, похоже, в моем понимании разрешения перегрузки не хватает. Интересно изменить второе объявление, чтобы принять X по значению заставляет его печатать «хорошо, хорошо» и заставляет его брать X по неконстантной ссылке печатает «плохо, хорошо». Очевидно, что полное удаление первого объявления возвращает его «хорошо, хорошо», так как другого выбора нет.

Так почему это происходит? И самое главное, если следующий код не работает, как вы можете перегрузить функцию этой сигнатурой?

#include <iostream>
#include <string>

class X {};

template<typename T>
inline std::string foo(T && rhs) {
return "bad";
}

inline std::string foo(const X & rhs) {
return "good";
}

int main() {
std::cout << foo(X()) << std::endl;
X x;
std::cout << foo(x) << std::endl;
return 0;
}

Редактировать:

Может быть, более окольным решением этого является косвенное решение. Избавиться от первой формы foo и использовать SFINAE, чтобы проверить, существует ли допустимая перегрузка, если она не вызывает foo_fallback,

7

Решение

Чтобы ответить на ваш вопрос на ответ Керре, вы можете попробовать использовать SFINAE:

#include <type_traits>
#include <string>

template <class T>
struct HasFooImpl_ {
template <typename C>
static std::true_type test(decltype(fooImpl(std::declval<C>()))*);
template <typename C>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
};

template <typename T>
using HasFooImpl = typename HasFooImpl_<T>::type;

template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}

template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}

Вы должны были бы реализовать функцию fooImpl для любого типа, который вы не хотите обрабатывать в общем.

Реализация была немного хитрой, я попробовал просто enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value во-первых, но для отступления !is_same<>::value дал мне ошибки компилятора, потому что он также пытался создать экземпляр decltype.

Эта реализация имеет одно предупреждение, которое вы можете или не хотите использовать: если T является конвертируемый к другому типу, который имеет fooImpl определил, что конверсия начнется.

Вы можете увидеть все это в действии здесь: http://ideone.com/3Tjtvj

Обновить:
если вы не хотите разрешать преобразования типов, на самом деле это становится проще:

#include <type_traits>
#include <string>

template <typename T> void fooImpl(T);

template <typename T>
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>;

template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}

template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}

Увидеть http://ideone.com/miaoop

3

Другие решения

Преобразование из X в const X считается хуже, чем прямое соответствие шаблонной перегрузки с T = X или же T = X &,

2

По вопросам рекламы [email protected]