功能模板扣除指南?

时间:2017-03-14 07:26:42

标签: c++ templates c++17 argument-deduction

我正在尝试编写一些模板化函数,这些函数接受可以构造std::basic_string的{​​{1}}或char数组。

我目前的解决方案是:

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>。这就是为什么我需要一种更简单的方法来告诉编译器。

2 个答案:

答案 0 :(得分:4)

经过一番研究后,最好的选择是使用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 - 数组也可用的方法(并且只是想确保无论参数如何,它都被正确调用)。在这种情况下,我只使用通用模板参数:

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

答案 1 :(得分:4)

咬紧牙关并使用2次重载。任何聪明的解决方案(如davidhigh所示)都会增加不必要的复杂性,可能会给下一位读者带来错误和混淆。

您只能写一次但多次阅读。写一行身体超负荷的小不方便值得采取非习惯性的错综复杂的智能方式

不要误会我的意思,我喜欢在C ++中找到这些智能解决方案,但如果我在生产代码中找到这个解决方案,那么我只需要几分钟它究竟是什么以及它做了什么,只是为了发现它只是以复杂的方式做了一件非常基本的事情,我会......好吧,我只是说我不会这样做。 t说一下代码作者的好话。在编写代码时懒惰会在维护,调试,扩展甚至使用代码时花费你的时间。

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