函数模板推导指南?
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_invoker
class试图做的(但它仍然需要一些改进,只是在努力):
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个重载。任何聪明的解决方案(如 所示确实存在)只会增加不必要的复杂性、潜在的错误和混淆下一个 reader。
你只写一次,却读了多次。写 1 行 body 超载的小不便值得针对非惯用的复杂 smart way.
不要误会我的意思,我喜欢在 C++ 中找到这些 智能解决方案 ,但是如果我在生产代码中找到这个解决方案,我会花几分钟的时间只是为了弄清楚它到底是什么以及它做了什么,只是为了发现它只是以复杂的方式做了本应该是非常基本的事情,我会......好吧,我只是说我不会说好关于代码作者的事情。在编写代码时偷懒,在维护、调试、扩展甚至使用代码时都会花费数倍的时间。
编写简单、惯用且易于理解的代码!
我正在尝试编写一些模板化函数,这些函数接受 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_invoker
class试图做的(但它仍然需要一些改进,只是在努力):
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 行 body 超载的小不便值得针对非惯用的复杂 smart way.
不要误会我的意思,我喜欢在 C++ 中找到这些 智能解决方案 ,但是如果我在生产代码中找到这个解决方案,我会花几分钟的时间只是为了弄清楚它到底是什么以及它做了什么,只是为了发现它只是以复杂的方式做了本应该是非常基本的事情,我会......好吧,我只是说我不会说好关于代码作者的事情。在编写代码时偷懒,在维护、调试、扩展甚至使用代码时都会花费数倍的时间。
编写简单、惯用且易于理解的代码!