Home > OS >  What mechanism prevents captured variables from being moved?
What mechanism prevents captured variables from being moved?

Time:08-03

I did some further experimentation on the topic of my previous question and have another one.

Consider the code below where, as I expeded, both l_ref are r_ref are const:

#include <string>
#include <iostream>

struct Func
{
    void operator()() const
    {
        static_assert(!std::is_const_v<decltype(v)>);

        static_assert(std::is_same_v<decltype(v), std::string>);

        static_assert(std::is_same_v<decltype(this), const Func*>);

        auto& l_ref = v;

        static_assert(std::is_same_v<decltype(l_ref), const std::string&>);

        //my idea was that it does not move because it is const r-value reference
        auto&& r_ref = std::move(v);

        static_assert(std::is_same_v<decltype(r_ref), const std::string&&>);

        std::cout << "v: " << v;
    }

    std::string v;
};

Now consider the following lambda were wired things start to happen and l_ref and r_ref are not const:

int main()
{
    std::string v = "abc";

    auto func = [v]()
    {
        static_assert(!std::is_const_v<decltype(v)>);

        static_assert(std::is_same_v<decltype(v), std::string>);

        auto& l_ref = v;

        static_assert(std::is_same_v<decltype(l_ref), std::string&>);

        auto&& r_ref = std::move(v);

        static_assert(std::is_same_v<decltype(r_ref), std::string&&>);

        //what happens here?
        auto a = std::move(v);

        static_assert(std::is_same_v<decltype(a), std::string>);

        std::cout << "v: " << v;
    };

    func();

    return 0;
}

v should not move at line auto a = std::move(v); because lambda is not mutable so it is something similar to void operator() const of the struct Func, but at the same time v is not const and std::move(v) is non-const r-value reference, so what mechanism prevents move constructor of std::string from being called?

CodePudding user response:

The move and copy constructors are both part of the same overload set. The compiler will either choose the copy or the move constructor according to the value category and const-ness of the arguments.

You second example is wrong and won't compile according to the standard. decltype(l_ref) and decltype(r_ref) are indeed const. See this live example.

MSVC seem to accept the code, but this is a compiler bug. You should report such problem to the microsoft msvc developpers.

To prove visual studio is wong and that the variable is indeed const, look at the assembly generated (unmangled):

call    std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &)

As you can see, it calls the copy constructor, not the move constructor according to the function parameter:

std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &
  • Related