Передача по значению всех типов, кроме string, в функцию шаблона
Я хочу определить шаблонную функцию, которая получает один аргумент, передаваемый по значению для всех типов, кроме std::string
(и const char*
).
template<typename T>
void foo( T value )
{
// some code using value
}
Версия std::string
должна вести себя точно так же, как версия template
, но ее параметр передается через const&
.
foo()
?
Лучшее, что я смог придумать, это обернуть код с помощью value
внутри другой функции, а затем вызвать ее внутри всех версий foo()
(версия template
и перегрузка std::string
). Есть ли другой способ? Например, можно ли вызвать версию template
из перегрузки std::string
?
EDIT
То, что я хочу знать, - это хорошее эмпирическое правило для предотвращения дублирования кода между различными специализациями и перегрузками. Что такое хороший образец для подражания? Должен ли я определить функцию-оболочку для тела и затем вызвать ее из всех перегрузок/специализаций, или есть другой способ?5 ответов:
Чтобы избежать дублирования кода, ответ по 101010 можно расширить, чтобы фактически вызвать шаблон из перегрузки:
#include <string> #include <iostream> #include <type_traits> #include <boost/core/demangle.hpp> template<typename T> void foo( T value ) { std::cout << "inside template" << std::endl; std::cout << boost::core::demangle(typeid(value).name()) << std::endl; } void foo(const std::string &value) { std::cout << "inside const string overload" << std::endl; foo<const std::string&>(value); } int main() { foo(10); foo(std::string("hello")); return 0; }
Вывод
inside template int inside const string overload inside template std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
Простое решение: обеспечить перегрузку для
std::string
:void foo( std::string const &value ) { // some code using value }
Я думаю, что вы ищете сигнатуру rvalue в C++ 11.
Это так просто, как:
#include <iostream> #include <string> template<typename T> void foo(T&& value) { std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl; std::cout << value << std::endl; } int main() { std::string text = "hello"; foo(text); foo(1); }
Вы можете передать параметр либо по ссылке, либо по значению, и правила rvalue будут использовать соответствующий тип.
Можно определить класс типа-признака, который преобразует
std::string
вstd::string&
и сохранит тип для всех других типов:template<class T> struct helper { typedef T type; }; template<> struct helper<std::string> { typedef std::string& type; // or const std::string& type if you want }; template<typename T> void foo( typename helper<T>::type value, T value2 ) { value = value2; } int main() { int a = 10; foo(a, 42); std::cout << a << std::endl; // prints 10 std::string s = "abc"; foo(s, std::string("def")); std::cout << s << std::endl; // prints def }
Полный пример: http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD: как отмечает @PiotrSkotnicki, наличие только одного параметра приводит к ошибке вывода типа. Тем не менее, я сохраню ответ, поскольку он может быть полезен в случае, если у вас действительно есть несколько параметров типа
T
или если вы согласны с указанием явного параметра шаблона дляfoo
.UPD2: чтобы решить проблему вычитания типа, вы можете добавить другую оболочку:
template<typename T> void foo_helper( typename helper<T>::type value ) { value = T(); } template<typename T> void foo(T& value) { foo_helper<T>(value); }
Это все еще может иметь некоторые проблемы,так что применимо ли это к вашей usecase, зависит от вас.