我了解完美转发在通常情况下是如何工作的:
template <typename T>
void f(T &&arg) {
E(std::forward<T>(arg)));
}
在不使用SFINAE或编写多个版本的情况下,是否可以完美地转发"非泛型"类型,例如某种形式的std::string
?
/* Likely some kind of template. */
/* template <typename T> */
void f(/* Something goes here*/ arg) {
E(std::forward</* Something goes here. */>(arg);
}
以下内容应为真:
f(std_str_obj); // Lvalue forwarded as const lvalue ref.
f("hello"); // Temporary forwarded as rvalue reference.
f(1); // Error, not a std::string or convertible to std::string.
我怀疑唯一的方法是仍然编写一个函数模板,并使用某种形式的SFINAE来限制它(在这种情况下,我可以自己解决一些问题(,但我想知道是否有一些简单的方法我遗漏了。
不,这样的事情是不可能的。
如果你的函数只采用一种指定的类型,最好只创建两个函数,不要试图用技巧巧妙地使用语言。
如果使用template,则f(1);
将int设置为模板参数。这不是你想要的。
除非我遗漏了什么,否则这应该适用于您正在寻找的内容。
void f(std::string&& arg) // Take care of rvalues
{
E(std::forward<std::string&&>(arg));
}
void f(std::string const& arg) // Take care of lvalues
{
E(std::forward<std::string const&>(arg));
}
我能够使用以下程序进行测试:
#include <iostream>
#include <string>
#include <utility>
void E(std::string const& s)
{
std::cout << "Came to E(std::string const& )n";
}
void E(std::string&& s)
{
std::cout << "Came to E(std::string&& )n";
}
void f(std::string&& arg) // Take care of rvalues
{
E(std::forward<std::string&&>(arg));
}
void f(std::string const& arg) // Take care of lvalues
{
E(std::forward<std::string const&>(arg));
}
int main()
{
std::string s1("abcd");
f(s1);
f("xyx");
}
我从运行程序中得到的输出:
到达E(std::string const&(来到E(std::string&&(
更新
用函数模板替换f
的两个重载实现也同样有效。
template <typename T>
void f(T&& arg)
{
E(std::forward<T>(arg));
}
int main()
{
std::string s1("abcd");
f(s1);
f("xyx");
}
如果我将f(1)
添加到main
,则会出现编译器错误,因为E
没有重载可以使用它。如果E
本身是一个函数模板,那么该策略将不起作用。
我认为,底线是,你必须阻止使用std::string
以外的任何东西,因为你不能在f
或E
级别上处理这些类型。
#include <utility>
#include <type_traits>
template <
class T
, class = typename std::enable_if<
std::is_convertible<std::decay_t<T>, std::string const&>::value
|| std::is_convertible<std::decay_t<T>, std::string &&>::value
>::type
>
void f (T&& t) { E (std::forward<T> (t)); }