Шаблон вычета руководство для функции?
Я пытаюсь написать некоторые шаблонные функции, которые принимают либо std::basic_string
или массив символов, из которого basic_string
может быть построен.
Мое текущее решение:
#include <string>
template<typename CharT>
void foo(std::basic_string<CharT> str)
{
(void)str; // do something with str
}
template<typename CharT>
void foo(CharT const * arr)
{
return foo(std::basic_string<CharT>{arr});
}
int main(void)
{
foo("hello");
foo(std::string{ "hello" });
foo(L"hello");
foo(std::wstring{ L"hello" });
}
Но это означает, что для каждой функции мне нужно написать другую функцию, которая вызывает первую. Это довольно раздражает; Есть ли более простой способ сделать это? Может быть, это может быть руководство по выводу шаблона, но, насколько я знаю, его не существует для функций, только для классов.
Первая шаблонная функция не достаточна, потому что вывод шаблона не выполняется: компилятор не может вывести CharT
в std::basic_string<CharT>
от CharT const *
, Вот почему мне нужен более простой способ сообщить об этом компилятору.
2 ответа
После нескольких исследований лучше всего использовать imo C++17. std::basic_string_view
:
template<typename CharT>
void foo(std::basic_string_view<CharT> str)
{
(void)str; // do something with str ...
// while remembering that string_view does not own the string
}
Так что забудьте о более раннем объяснении ниже, если у вас есть доступ к 17-компилятору C++.
Здесь нужно рассмотреть два случая. В первом случае вы не хотите делать что-то особенное с базовой строкой, а применяете только те методы, которые также доступны для char
-array (и просто хочу убедиться, что он вызывается правильно, независимо от параметров). В этом случае я бы просто использовал общий параметр шаблона:
template<typename string_type
/* possibly some SFINAE to allow/disallow certain types */>
auto foo(string_type s)
{
std::cout << s << std::endl;
}
Второй случай - вы действительно хотите сделать какую-то специальную операцию со строкой, которой нет в массиве char. В этом случае вам нужна перегрузка для basic_string
, но вы, вероятно, хотите написать это только один раз, а не для каждой функции, которую вы используете. Это то, что следующее string_invoker
класс пытается это сделать (но он все еще нуждается в некотором улучшении, просто работая над этим):
template<typename method>
struct string_invoker_impl
{
string_invoker_impl(method m) : m(m) {}
template<typename CharT>
auto operator()(std::basic_string<CharT> str) const
{
return m(str);
}
template<typename CharT>
auto operator()(CharT const * arr) const
{
return operator()(std::basic_string<CharT>{arr});
}
//possibly further methods for non-const array's, modification, etc.
method m;
};
auto string_invoker = [](auto m) { return string_invoker_impl<decltype(m)>{m}; };
auto foo_impl = [](auto str) {std::cout<< str <<std::endl; };
auto foo = string_invoker(foo_impl);
//you can merge the previous two calls also in a single one:
//auto foo = string_invoker( [](auto str) {std::cout<< str <<std::endl; });
int main(void)
{
foo("hello");
foo(std::string{ "hello" });
//foo(L"hello"); //need std::wcout, thus it fails with std::cout
//but it's no general problem, just overload your foo_impl function
//foo(std::wstring{ L"hello" });
}
Просто укусите пулю и используйте 2 перегрузки. Любое умное решение (которое, как показал Дэвид Хай, существует) только добавит ненужной сложности, потенциальных ошибок и путаницы следующему читателю.
Вы пишете только один раз, но читаете несколько раз. Небольшое неудобство написания 1 строки перегрузки тела стоит того, чтобы противостоять не-идиоматическому запутанному умному способу.
Не поймите меня неправильно, я люблю находить эти умные решения в C++, но если бы я нашел это решение в рабочем коде, мне потребовалось бы несколько хороших минут, чтобы просто выяснить, что это за хрень и что она делает, только чтобы выяснить, что это просто делает то, что должно было быть очень простым, сложным способом, я бы... ну, скажем так, я бы не сказал ничего хорошего об авторе кода. Лень при написании кода обойдется вам дороже, если вы будете поддерживать, отлаживать, расширять или даже использовать код.
Напишите простой, идиоматичный и легкий для понимания код!