std::forward 和带有非常量引用参数的构造函数

发布于 2024-12-19 08:04:08 字数 786 浏览 1 评论 0原文

右值引用简介中,完美转发被提出作为将右值 5 转发到具有非常量引用参数的构造函数的理想解决方案。

但是:

#include <memory>
#include <iostream>
#include <utility>

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
}

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }
};


int main() {
    std::shared_ptr<X> p = factory<X>(5);
}

在 XCode 4.2 和 G++ 4.6.1 中失败,没有已知的从 int 到 int& 的转换,而:

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}

编译。我做错了什么?

In A brief Introduction to Rvalue References, perfect forwarding is proposed as the ideal solution to forward rvalue 5 to a constructor with a non-const reference argument.

But:

#include <memory>
#include <iostream>
#include <utility>

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
}

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }
};


int main() {
    std::shared_ptr<X> p = factory<X>(5);
}

fails in XCode 4.2 ans G++ 4.6.1 with no known conversion from int to int&, whereas:

template <class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
   return std::shared_ptr<T>(new T(/*no forwarding*/a1));
}

compiles. What did I get wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

迷鸟归林 2024-12-26 08:04:08

您不能将右值绑定到非常量左值引用。本文不建议为此使用完美转发,因为这是不可能的。完美转发将左值转发为左值,将右值转发为右值:

在这里,forward 保留了参数的左值/右值性质
被传递到工厂。如果将右值传递给工厂,则
右值将在前向的帮助下传递给 T 的构造函数
功能。类似地,如果将左值传递给工厂,则它是
作为左值转发到 T 的构造函数。

由于示例中的构造函数仅接受左值,因此您只能将左值传递到工厂函数中。传递右值会将其作为右值转发,这将是格式错误的,因为无法将右值传递给该构造函数。

You cannot bind rvalues to non-const lvalue references. The article does not suggest using perfect forwarding for that, because that's not possible. Perfect forwarding forwards lvalues as lvalues, and rvalues as rvalues:

Here, forward preserves the lvalue/rvalue-ness of the argument that
was passed to factory. If an rvalue is passed to factory, then an
rvalue will be passed to T's constructor with the help of the forward
function. Similarly, if an lvalue is passed to factory, it is
forwarded to T's constructor as an lvalue.

Since the constructor in your example only takes lvalues, you can only pass lvalues into the factory function. Passing an rvalue will forward it as an rvalue, and that will be ill-formed because there's no way to pass an rvalue to that constructor.

似梦非梦 2024-12-26 08:04:08

完美转发被提出作为将右值 5 转发到具有非常量引用参数的构造函数的理想解决方案。

我不认为完美转发就是这个意思。这篇文章即使是正确的,也不能暗示这一点,即使是远程的。

相反,它意味着它可以将右值引用作为右值转发,以便调用 move-constructor 或采用 rvaluereferences 的构造函数/函数。

所以你应该尝试这个:

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }

    //this is a constructor which takes rvalue references
    X(int&& i){ 
        std::cout<<"X("<<i<<")\n";
    }
};

也就是说,从工厂中,应该调用第二个构造函数,而不是你编写的构造函数。

顺便说一句,在这种情况下,构造函数没有多大意义,因为参数类型 int 是基本类型。

右值引用作为参数类型用于定义管理资源的类的移动构造函数和移动分配。如果用户定义的类不管理资源,那么移动语义就没有意义。

perfect forwarding is proposed as the ideal solution to forward rvalue 5 to a constructor with a non-const reference argument.

I don't think perfect forwarding means that. The article, if it is correct, cannot suggest that, even remotely.

Rather, it means it can forward rvalue references as rvalues, so to invoke move-constructor or a constructor/function which takes rvalue references .

So you should try this:

class X {
public:
    X(int& i){
        std::cout<<"X("<<i<<")\n";
    }

    //this is a constructor which takes rvalue references
    X(int&& i){ 
        std::cout<<"X("<<i<<")\n";
    }
};

That is, from factory, the second constructor should be invoked, not the one which you've written.

By the way, in this case, the constructor doesn't makes sense much, because the parameter type int is a fundamental type.

Rvalue referencs as parameter type is used to defined move-constructor and move-assignment of classes which manages resources. If user-defined class doesn't manage resource, then move-semantics doesn't make sense.

岁月苍老的讽刺 2024-12-26 08:04:08

暂时忽略右值引用,假装这是允许的:

void modify_int(int& i)
{
    i = 1;
}

void foo(int& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(i); // makes i = 1

// illegal in standard C++, cannot bind a temporary to a non-const reference
foo(5); // makes the temporary integer equal to 1

您可以看到临时对象被修改,这完全没问题。然而,这种绑定在 C++ 中是非法的,因为它通常是不需要的(毕竟读起来好像 5 被更改为 1)。

所有右值引用所做的都是启用临时值与引用的绑定,但这是安全的,因为我们知道我们正在处理一个应被视为临时的值:

void modify_int(int& i)
{
    i = 1;
}

void foo(int&& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(std::move(i)); // makes i = 1 (std::move makes it an rvalue)

// legal in C++11, temporary is bound to rvalue-reference
foo(5); // makes the temporary integer equal to 1

请注意,在此版本的 foo 中,传递到 modify_int 仍然完全没问题。一旦进入函数内部,它是右值引用而不是左值引用这一事实就无关紧要了:我们仍然有一个对象要引用。 在模板中使用转发来保留值类别

void test(int& i) {} // lvalue version of test
void test(int&& i) {} // rvalue version of test

template <typename T>
void foo(T&& x)
{
    // if x was an lvalue, forward does nothing;
    // if x was an rvalue, forward std::move's it 
    test(std::forward<T>(x)); 
}

int i = 7;
foo(i); // calls lvalue version of test

foo(5); // calls rvalue version of test

没有转发的代码是类似于我答案中的第二个片段。一旦进入工厂函数,a1 只是一个常规左值,并且可以很好地绑定到构造函数引用。但通过转发,它会变回右值(因为 factory(5) 使用右值调用它),它无法绑定到左值引用,从而导致错误。

Ignore rvalue-references for a second, and instead pretend this was allowed:

void modify_int(int& i)
{
    i = 1;
}

void foo(int& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(i); // makes i = 1

// illegal in standard C++, cannot bind a temporary to a non-const reference
foo(5); // makes the temporary integer equal to 1

You can see that the temporary object gets modified, which is perfectly fine. However, this binding was made illegal in C++ because its usually not desired (it reads as if 5 was being changed to 1, after all).

All rvalue-references do is enable the binding of temporary values to references, but safely because we understand that we're dealing with a value that should be considered temporary:

void modify_int(int& i)
{
    i = 1;
}

void foo(int&& x)
{
    modify_int(x); // okay, modify_int references x
}

int i = 7;
foo(std::move(i)); // makes i = 1 (std::move makes it an rvalue)

// legal in C++11, temporary is bound to rvalue-reference
foo(5); // makes the temporary integer equal to 1

Note that in this version of foo, passing to modify_int is still perfectly fine. Once inside the function, the fact that it was an rvalue-reference instead of an lvalue-reference is irrelevant: we still have an object to refer to. Forwarding is used in templates to preserve the value category:

void test(int& i) {} // lvalue version of test
void test(int&& i) {} // rvalue version of test

template <typename T>
void foo(T&& x)
{
    // if x was an lvalue, forward does nothing;
    // if x was an rvalue, forward std::move's it 
    test(std::forward<T>(x)); 
}

int i = 7;
foo(i); // calls lvalue version of test

foo(5); // calls rvalue version of test

Your code without forwarding is similar to the second snippet in my answer. Once inside the factory function, a1 is just a regular lvalue, and binds to the constructor reference just fine. But with forwarding, it turns back into an rvalue (because factory(5) calls it with an rvalue), which cannot bind to the lvalue-reference, resulting in an error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文