【问题标题】:Wrapper for perfect forwarding constructor完美转发构造函数的包装器
【发布时间】:2018-04-13 15:55:08
【问题描述】:

我想为 B 类的构造函数做一个包装器。

由于 B 类的某些部分是可替换的,因此我将这些部分的每个实现分组到几个 A 类中。

我使用完美转发来提供 A 的默认实现来构造 B。

问题是包装器不起作用。如何解决这个问题?

谢谢。

以下也是https://godbolt.org/g/AWwtbf

template<typename T>
class A {
public:
    A() { _x = 2; }
    int _x;
};

template<typename T, typename C=A<T> >
class B {
public:
    explicit B(T x, C&& a = A<T>())
            : _x(x), _a(a) {
    }
    T _x;
    A<T>& _a;
};

template<typename T, typename C=A<T> >
B<T, A<T>> make_b(T x, C&& c = A<T>()) {
    return B<int, A<int>>(x, c);
};

int main() {
    B<int, A<int>> b1(1);  // this works.
    auto b2 = make_b(1);  // this doesn't
}

错误:

error: cannot bind rvalue reference of type 'A<int>&&' to lvalue of type 'A<int>'

     return B<int, A<int>>(x, c);

【问题讨论】:

  • 阅读this。你似乎有一些基本的误解
  • 阅读第1页和第2页后,我认为A&lt;T&gt;()是一个右值。它代表调用A的构造函数而不是实际的结果,它是A的一个实例。如果A 有一个移动构造函数,它接受这个右值并创建A 的实际实例,并将B::_a 的类型更改为A&lt;T&gt; 而不是A&lt;T&gt;&amp;,就可以了吗?
  • 这听起来太复杂了。 B 和 A 将同时创建。我只会要求 B 有一个 A 作为属性。也许还可以公开B::_a。这样,创建 B 也会创建 A,但我可以灵活选择创建哪种 A。
  • 你应该继续阅读,如果你打算用 C++ 做任何严肃的事情,这将是值得的。每个表达式都有一个值类别,它是左值或右值。 A&lt;T&gt;() 确实是一个右值(它的值类别),也是一个A&lt;T&gt;(它的类型)。这是完美转发的基础,但只是故事的一半。

标签: c++ constructor wrapper perfect-forwarding


【解决方案1】:

如何完善转发内容?

  1. 将参数的类型设为template 参数。
  2. 通过右值引用 (&amp;&amp;) 接受参数。
  3. std::forward 论据。

你有nr。 1和2但忘记了nr。 3:

template<typename T, typename C=A<T> >
B<T, A<T>> make_b(T x, C&& c = A<T>()) {
    return B<T, A<int>>(x, std::forward<C>(c));
};

如何避免悬空引用?

此代码创建一个悬空引用,即对已经是 gone 的对象的引用:

    explicit B(T x, C&& a = A<T>())
            : _x(x), _a(a) {
    }
    T _x;
    A<T>& _a;

考虑一下这里发生了什么:创建了一个临时的A,右值引用a被用在一个左值表达式(a)中,所以它变成了一个左值引用,A&amp; _a随后愉快地绑定到它.当完整表达式完成时(在第一个;),临时的A 实例被销毁,A&amp; _a 指的是一个不存在的对象。如果在此之后使用_a,则行为未定义。

在您了解value categoriesobject lifetimereference collapsing 规则之前,请避免将引用作为数据成员。

只需按值存储A

    explicit B(T x, C&& a = A<T>())
            : _x(x), _a(std::move(a)) {
    }
    T _x;
    A<T> _a;

或在B 之外实例化A 并将其作为左值引用传递:

    explicit B(T x, C& a) : _x(x), _a(a) {}
    C& _a;

【讨论】:

    猜你喜欢
    • 2015-06-05
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多