Как специализировать шаблон функции, который принимает универсальный ссылочный параметр?
foo.hpp:
template<typename T>
void foo(T && t) // universal reference parameter
foo.cpp
template<>
void foo<Class>(Class && class) {
// do something complicated
}
Вот, Class
больше не выводимый тип и, следовательно, Class
именно так; это не может быть Class &
, поэтому правила свертывания ссылок здесь мне не помогут. Я мог бы, возможно, создать другую специализацию, которая занимает Class &
параметр (я не уверен), но это подразумевает дублирование всего кода, содержащегося в foo
для каждой возможной комбинации ссылок rvalue / lvalue для всех параметров, чего должны избегать универсальные ссылки.
Есть ли способ сделать это?
Чтобы быть более конкретным о моей проблеме в случае, если есть лучший способ ее решения:
У меня есть программа, которая может подключаться к нескольким игровым серверам, и каждый сервер, по большей части, вызывает все с одинаковым именем. Тем не менее, у них есть несколько разные версии для нескольких вещей. Есть несколько различных категорий, которыми могут быть эти вещи: перемещение, элемент и т. Д. Я написал общий набор функций «переместить строку для перемещения» для вызова внутреннего кода, и мой код интерфейса сервера имеет аналог функции. Однако некоторые серверы имеют свои собственные внутренние идентификаторы, с которыми они взаимодействуют, некоторые используют строки, а некоторые используют оба в разных ситуациях.
Теперь я хочу сделать это немного более общим.
Я хочу быть в состоянии назвать что-то вроде ServerNamespace::server_cast<Destination>(source)
, Это позволило бы мне бросить из Move
к std::string
или же ServerMoveID
, Внутренне мне может потребоваться сделать копию (или переместиться), потому что некоторые серверы требуют, чтобы я вел историю отправленных сообщений. Универсальные ссылки кажутся очевидным решением этой проблемы.
Заголовочный файл, о котором я сейчас думаю, будет представлен просто так:
namespace ServerNamespace {
template<typename Destination, typename Source>
Destination server_cast(Source && source);
}
И файл реализации будет определять все легальные преобразования как шаблонные специализации.
На мой взгляд, лучшим решением является использование системы диспетчеризации тегов, в которой вы перегружаете теги, а не фактический тип:
struct foo {
struct tag {};
};
struct bar {
struct tag {};
};
template<typename Destination, typename Source>
Destination server_cast(Source && source, foo::tag) {
// foo
}
template<typename Destination, typename Source>
Destination server_cast(Source && source, bar::tag) {
// bar
}
template<typename Destination, typename Source>
Destination server_cast(Source && source) {
return server_cast<Destination>(std::forward<Source>(source), typename std::remove_reference<Source>::type::tag());
}
Самое расширяемое, что нужно сделать, это создать специализацию класса шаблона.
template< class X > struct Whatever {
void f(){ ... }
};
template<> struct Whatever<UserType> {
void f(){ ... }
};
Причина, по которой я говорю, что это наиболее расширяемая причина, заключается в том, что вы можете добавить специализацию где угодно, внутри или снаружи файла, который определяет что угодно.
Это является дополнительным, а не исключительным, для решения по отправке тегов, предложенного Pubby.