Шаблон вычета руководство для функции?

Я пытаюсь написать некоторые шаблонные функции, которые принимают либо 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" });
}

DEMO

Просто укусите пулю и используйте 2 перегрузки. Любое умное решение (которое, как показал Дэвид Хай, существует) только добавит ненужной сложности, потенциальных ошибок и путаницы следующему читателю.

Вы пишете только один раз, но читаете несколько раз. Небольшое неудобство написания 1 строки перегрузки тела стоит того, чтобы противостоять не-идиоматическому запутанному умному способу.

Не поймите меня неправильно, я люблю находить эти умные решения в C++, но если бы я нашел это решение в рабочем коде, мне потребовалось бы несколько хороших минут, чтобы просто выяснить, что это за хрень и что она делает, только чтобы выяснить, что это просто делает то, что должно было быть очень простым, сложным способом, я бы... ну, скажем так, я бы не сказал ничего хорошего об авторе кода. Лень при написании кода обойдется вам дороже, если вы будете поддерживать, отлаживать, расширять или даже использовать код.

Напишите простой, идиоматичный и легкий для понимания код!

Другие вопросы по тегам