函数模板推导指南?

Template deduction guide for function?

我正在尝试编写一些模板化函数,这些函数接受 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 const * 推导 std::basic_string<CharT> 中的 CharT。这就是为什么我需要一种更简单的方法来将其告知编译器。

经过更多研究,我认为最好的选择是使用 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
}

因此,如果您可以访问 C++17 编译器,请忽略下面的旧解释。



这里有两种情况需要考虑。 第一种情况 是你真的不想对基本字符串做一些特殊的事情,而是你只应用也可用于 char-array 的方法(并且只是想以确保无论参数如何都能正确调用它)。在这种情况下,我会简单地使用通用模板参数:

template<typename string_type
        /* possibly some SFINAE to allow/disallow certain types */>
auto foo(string_type s)
{
    std::cout << s << std::endl;
}

第二种情况是你真的想对字符数组不存在的字符串做一些特殊操作。在这种情况下,您需要 basic_string 的重载,但您可能只想编写一次,而不是为您使用的每个函数编写一次。这是下面的string_invokerclass试图做的(但它仍然需要一些改进,只是在努力):

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个重载。任何聪明的解决方案(如 所示确实存在)只会增加不必要的复杂性、潜在的错误和混淆下一个 reader。

你只写一次,却读了多次。写 1 行 body 超载的小不便值得针对非惯用的复杂 smart way.

不要误会我的意思,我喜欢在 C++ 中找到这些 智能解决方案 ,但是如果我在生产代码中找到这个解决方案,我会花几分钟的时间只是为了弄清楚它到底是什么以及它做了什么,只是为了发现它只是以复杂的方式做了本应该是非常基本的事情,我会......好吧,我只是说我不会说好关于代码作者的事情。在编写代码时偷懒,在维护、调试、扩展甚至使用代码时都会花费数倍的时间。

编写简单、惯用且易于理解的代码!