编辑:解决看到评论 - 不知道如何标记解决与答案.
在c ++ 0x中观看有关完美转发/移动语义的第9频道视频之后,我认为这是编写新分配运算符的好方法.
#include <string>
#include <vector>
#include <iostream>
struct my_type
{
my_type(std::string name_)
: name(name_)
{}
my_type(const my_type&)=default;
my_type(my_type&& other)
{
this->swap(other);
}
my_type &operator=(my_type other)
{
swap(other);
return *this;
}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
void operator=(const my_type&)=delete;
void operator=(my_type&&)=delete;
};
int main()
{
my_type t("hello world");
my_type t1("foo bar");
t=t1;
t=std::move(t1);
}
Run Code Online (Sandbox Code Playgroud)
这应该允许将r值和const分配给它.通过使用适当的构造函数构造一个新对象,然后使用*this交换内容.这对我来说听起来很合理,因为没有数据被复制超过它需要的数量.指针算术很便宜.
但是我的编译器不同意.(g ++ 4.6)我得到了这些错误.
copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = …Run Code Online (Sandbox Code Playgroud) 虽然T&&与模板一起使用作为转发参考或通用引用(如Scott Meyers所称),但我auto&&在代码示例中看到了一些博客.我认为auto自己应该足够了,然而,CbCon 2014中的 Herb Sutter 说:永远不要auto&&用于局部变量
这是为什么?
看到所有回复,我觉得我应该问对方.尽管有一般编码指南,但是在函数体内使用auto &&以获得代码正确性和可维护性是有任何好的用例.
为什么以下代码有效:
template<typename T1>
void foo(T1 &&arg) { bar(std::forward<T1>(arg)); }
std::string str = "Hello World";
foo(str); // Valid even though str is an lvalue
foo(std::string("Hello World")); // Valid because literal is rvalue
Run Code Online (Sandbox Code Playgroud)
但不是:
void foo(std::string &&arg) { bar(std::forward<std::string>(arg)); }
std::string str = "Hello World";
foo(str); // Invalid, str is not convertible to an rvalue
foo(std::string("Hello World")); // Valid
Run Code Online (Sandbox Code Playgroud)
为什么示例2中的左值不会以与示例1中相同的方式解析?
另外,为什么标准认为需要在std :: forward与简单推导中提供参数类型很重要?无论类型如何,简单地呼唤前方都表现出意图.
如果这不是标准的东西,只是我的编译器,我使用msvc10,这将解释蹩脚的C++ 11支持.
谢谢
编辑1:将文字"Hello World"更改为std :: string("Hello World")以生成rvalue.
Herb Sutter 回归基础!CppCon 上的现代C++演示要点讨论了传递参数的不同选项,并将其性能与写作/教学的简易性进行了比较."高级"选项(在所有测试的案例中提供最佳性能,但对大多数开发人员来说难以编写)是完美转发,给出了示例(PDF,第28页):
class employee {
std::string name_;
public:
template <class String,
class = std::enable_if_t<!std::is_same<std::decay_t<String>,
std::string>::value>>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<std::string &, String>::value) {
name_ = std::forward<String>(name);
}
};
Run Code Online (Sandbox Code Playgroud)
该示例使用带转发引用的模板函数,模板参数String使用约束enable_if.然而,约束似乎是不正确的:似乎只有在String类型不是a 时才使用此方法std::string,这没有任何意义.这将意味着该std::string成员可以使用设置什么,但一个std::string值.
using namespace std::string_literals;
employee e;
e.set_name("Bob"s); // error
Run Code Online (Sandbox Code Playgroud)
我考虑的一个解释是,有一个简单的拼写错误,而且约束的目的是std::is_same<std::decay_t<String>, std::string>::value代替!std::is_same<std::decay_t<String>, std::string>::value.然而,这意味着setter不能使用,例如,const char *它显然是打算使用这种类型,因为这是在演示文稿中测试的案例之一.
在我看来,正确的约束更像是:
template <class String,
class = std::enable_if_t<std::is_assignable<decltype((name_)),
String>::value>>
void set_name(String &&name) …Run Code Online (Sandbox Code Playgroud) 假设我有两个structs:
struct X {};
struct Y { X x; }
Run Code Online (Sandbox Code Playgroud)
我有功能:
void f(X&);
void f(X&&);
Run Code Online (Sandbox Code Playgroud)
我怎样写一个函数g()是需要Y&或Y&&只有完美的转发X&或X&&至f(),分别为:
template <typename T>
void g(T&& t) {
if (is_lvalue_reference<T>::value) {
f(t.x);
} else {
f(move(t.x));
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码说明了我的意图,但随着参数数量的增加,它的可扩展性不高.有没有办法让它完美转发并使其可扩展?
据我所知,在C++ 11中有效实现构造函数的两种常用方法是使用其中两种
Foo(const Bar& bar) : bar_{bar} {};
Foo(Bar&& bar) : bar_{std::move(bar)} {};
Run Code Online (Sandbox Code Playgroud)
或者只是一个时尚的
Foo(Bar bar) : bar_{std::move(bar)} {};
Run Code Online (Sandbox Code Playgroud)
使用第一个选项产生最佳性能(例如,在左值的情况下希望是单个副本,在rvalue的情况下希望单个移动),但是对于N个变量需要2个N重载,而第二个选项只需要一个函数通过左值时的额外动作.
在大多数情况下,这不应该产生太大的影响,但肯定两种选择都不是最佳选择.但是,也可以执行以下操作:
template<typename T>
Foo(T&& bar) : bar_{std::forward<T>(bar)} {};
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是允许可能不需要的类型的变量作为bar参数(这是我确信使用模板专门化很容易解决的问题),但无论如何,性能是最佳的,代码随着变量的数量线性增长.
为什么没有人为此目的使用像forward这样的东西?这不是最优化的方式吗?
我想出了以下代码将R()alike转换为类似void()callable:
#include <utility>
template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
// ^-- is it ok?
int main()
{
auto f = discardable([n=42]() mutable { return n--; });
f();
}
Run Code Online (Sandbox Code Playgroud)
我很担心被引用捕获.
callable永远不会复制,并且在其生命周期结束后从未使用过吗?这是标记的C++ 14,但适用于所有以下标准.
我正在尝试完美转发,发现
std::forward()需要两个重载:
过载 1:
template <typename T>
inline T&& forward(typename
std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
Run Code Online (Sandbox Code Playgroud)
重载nr.2:
template <typename T>
inline T&& forward(typename
std::remove_reference<T>::type&& t) noexcept
{
static_assert(!std::is_lvalue_reference<T>::value,
"Can not forward an rvalue as an lvalue.");
return static_cast<T&&>(t);
}
Run Code Online (Sandbox Code Playgroud)
现在,完美转发的典型场景是
template <typename T>
void wrapper(T&& e)
{
wrapped(forward<T>(e));
}
Run Code Online (Sandbox Code Playgroud)
当然,您知道何时wrapper()实例化T取决于传递给它的参数是左值还是右值。如果它是type的左值U,T则推导为U&。如果是右值,T则推导为U。
无论如何-在-的范围内wrapper()- e是一个左值,因此它始终使用的第一个重载std::forward()。
现在我的问题是:
使用(需要)第二重载的有效方案是什么?
我正在试验C++ 11的新功能.在我的设置中,我真的很想使用继承构造函数,但遗憾的是没有编译器实现这些.因此,我试图模拟相同的行为.我可以这样写:
template <class T>
class Wrapper : public T {
public:
template <typename... As>
Wrapper(As && ... as) : T { std::forward<As>(as)... } { }
// ... nice additions to T ...
};
Run Code Online (Sandbox Code Playgroud)
这很有用......大多数时候.有时使用Wrapper类的代码必须使用SFINAE来检测如何Wrapper<T>构造这样的代码.然而,存在以下问题:就重载决策而言,构造函数Wrapper<T>将接受任何参数 - 但是如果不能使用那些构造类型,则编译失败(并且这不包括在SFINAE中)T.
我试图使用有条件地启用构造函数模板的不同实例化 enable_if
template <typename... As, typename std::enable_if<std::is_constructible<T, As && ...>::value, int>::type = 0>
Wrapper(As && ... as) // ...
Run Code Online (Sandbox Code Playgroud)
哪个工作正常,只要:
T是publicT 不是抽象的我的问题是:如何摆脱上述两个限制?
我试图克服所述第一通过检查(使用SFINAE和sizeof())是否表达new T(std::declval<As &&>()...) …
c++ templates template-meta-programming perfect-forwarding c++11
如何在C++ 14中编写通用转发lambda?
[](auto&& x) { return x; }
Run Code Online (Sandbox Code Playgroud)
在函数体内部,x是一个左值,所以这不起作用.
[](auto&& x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)
这正确地转发了lambda中的引用,但它总是按值返回(除非编译器省略了副本).
[](auto&& x) -> decltype(x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)
这将返回与参数相同的类型(可能-> auto&&也会起作用)并且似乎正常工作.
[](auto&& x) noexcept -> decltype(x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)
添加是否noexcept使这个lambda更适用,因此严格优于#3?