1

我经常遇到传值和移动习语:

struct Test
{
    Test(std::string str_) : str{std::move(str_)} {}
    std::string str;
};

但在我看来,通过 const 引用或 rvalue 引用在某些情况下可以保存副本。就像是:

struct Test1
{
    Test1(std::string&& str_) : str{std::move(str_)} {}
    Test1(std::string const& str_) : str{str_} {}
    std::string str;
};

或者也许使用转发引用来避免编写两个构造函数。就像是:

struct Test2
{
    template<typename T> Test2(T&& str_) : str{std::forward<T>(str_)} {}
    std::string str;
};

是这样吗?如果是这样,为什么不使用它呢?

此外,看起来 C++20 允许使用自动参数来简化语法。我不确定在这种情况下的语法是什么。考虑:

struct Test3
{
    Test3(auto&& str_) : str{std::forward<decltype(str_)>(str_)} {}
    std::string str;
};

struct Test4
{
    Test4(auto str_) : str{std::forward<decltype(str_)>(str_)} {}
    std::string str;
};

编辑:

建议的问题是信息丰富的,但他们没有提到“汽车”案例。

4

2 回答 2

2

但在我看来,通过 const 引用或 rvalue 引用在某些情况下可以保存副本。

确实,但它需要更多的重载(甚至最糟糕的几个参数)。

按价值传递和移动成语(在最坏的情况下)有一个额外的移动。大多数时候这是一个很好的权衡。

也许使用转发引用来避免编写两个构造函数。

转发引用有其自身的缺陷:

  • 不允许{..}参数的语法,因为{..}它没有类型。
    Test2 a({5u, '*'}); // "*****"
    
    是不可能的。
  • 不限于有效类型(需要额外requires或 SFINAE)。
    Test2 b(4.2f); // Invalid, but `std::is_constructible_v<Test2, float>` is (falsely) true.
    
    会在构造函数内部产生错误,而不是在调用站点产生错误(因此错误消息不太清楚,并且 SFINAE 不可能)。
  • 对于构造函数,它可以优先于复制构造函数(对于非常量左值)
    Test2 c(a); // Call Test2(T&&) with T=Test2&
                // instead of copy constructor Test2(const Test2&)
    
    会产生错误,因为std::string不能从Test2&.
于 2021-10-27T16:53:23.807 回答
0

除了Jarod42答案和建议的欺骗(1)之外,您可以通过限制模板参数包的有效类型来克服前向引用方法的所示缺陷。

#include <string>
#include <concepts>

struct Test
{
    template<class... Args>
        requires std::constructible_from<std::string, Args...>
    Test(Args&&... str_)
        : str( std::forward<Args>(str_)... )
    {}
    std::string str;
};

int main()
{
    Test a{"So far, so good..."};

    Test b{5u, '*'};      // -> "*****"

//    Test b({5u, '*'});  // It works too.
    
    Test c{b};

//    Test d(4.2f);
// error: no matching constructor for initialization of 'Test'
}

(1) pass-by-value-and-then-move 结构是一个坏习惯吗?
按值传递和 std::move 优于按引用传递的优点

于 2021-10-28T09:35:55.877 回答