У меня есть функция, которая может принимать любой тип по универсальной ссылке, и я хотел бы перегрузить его для определенных типов (некоторые из них сами по себе шаблонизированы, хотя я не думаю, что это важно здесь). К сожалению, я не могу понять, что перегрузки должны быть решены в правильном порядке.
Я бы предположил, что вторая декларация 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
,
Чтобы ответить на ваш вопрос на ответ Керре, вы можете попробовать использовать 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
Преобразование из X
в const X
считается хуже, чем прямое соответствие шаблонной перегрузки с T = X
или же T = X &
,