首页 > 解决方案 > 从 const 右值移动

问题描述

我遇到了一些在函数参数中使用 const rvalue 引用的代码,std::function该函数参数在 lambda 中传递。令人困惑的是,它随后std::move调用了这个传入的参数。像这样的东西:

using CallbackFn = std::function<void()>;
using AnotherCbFn = std::function<void(int)>;

void bar(AnotherCbFn&& cb) {
    // doSomething();
}

void foo(CallbackFn const&& cb) {
    // Some code
    bar([ x = std::move(cb) /* <-- What's this? */](int value){
        x();
    });
}

void baz() {
    foo([](){
        // doSomethingMore();
    });
}

传入常量值引用然后调用std::move它们的目的是什么?所以我尝试了一个更简单的代码片段来看看在这种情况下会发生什么

#include <utility>
#include <string>
#include <cstdio>
#include <type_traits>

struct Foo {
    Foo() = default;

    Foo(Foo&& o) {
        str = std::move(o.str); // calls the move assignment operator
        std::printf("Other [%s], This [%s]\n", o.str.data(), str.data());
    }

    Foo(Foo const&& o) {
        str = std::move(o.str); // calls the copy assignment operator
        std::printf("Other [%s], This [%s]\n", o.str.data(), str.data());
    }

    private:
    std::string str = "foo";
};

template <typename T>
void f(T&& x) {
    if constexpr(std::is_const_v<T>) {
        std::printf("Const rvalue\n");
        auto temp = std::move(x);
    } else {
        std::printf("non-const rvalue\n");
        auto temp = std::move(x);        
    }
}

Foo const getConstRvalue() {
    return Foo();
}

Foo getNonConstRvalue() {
    return Foo();
}

int main() {
    f(getConstRvalue());
    f(getNonConstRvalue());
}

这产生了输出:

Const rvalue
Other [foo], This [foo]
non-const rvalue
Other [], This [foo]

在godbolt(这里)检查组件确认发生了什么。Foo(const&&)调用 的复制赋值运算符std::string

调用 std::__cxx11::basic_string<char, std::char_traits, std::allocator >::operator=(std::__cxx11::basic_string<char, std::char_traits, std::allocator > const&)

whileFoo(Foo&&)调用 的移动赋值运算符std::string

调用 std::__cxx11::basic_string<char, std::char_traits, std::allocator >::operator=(std::__cxx11::basic_string<char, std::char_traits, std::allocator >&&)

认为请纠正我!)const-lvalue 函数参数也可以绑定到 const rvalue 参数(以及非 const rvalue、const lvalue 和 non-const lvalue),这就是为什么在由于Foo(const&&)const-rvaluestd::string不能绑定到移动赋值运算符中的非 const rvalue 的情况。

那么,传递 const 右值引用然后调用std::move它的目的是什么,因为调用std::move通常意味着在此之后不应该使用该值,在这种情况下,实际上涉及的是副本而不是所需的移动语义?是否有一些微妙的语言机制在起作用?

标签: c++move

解决方案


std::move 什么也不移动,它只是将左值(对右值cb的引用)重新解释为您忘记在代码片段中显示的某些bar函数所期望的右值。

我怀疑它看起来像:

void bar(CallbackFn const&& cb) {
  ...
}

推荐阅读