Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

What mechanism prevents captured variables from being moved?

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

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 &
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading