3
#include<iostream>
#include<vector>

class Container {
    int * m_Data;
public:
    Container() {
        //Allocate an array of 20 int on heap
        m_Data = new int[20];

        std::cout << "Constructor: Allocation 20 int" << std::endl;
    }

    ~Container() {
        if (m_Data) {
            delete[] m_Data;
            m_Data = NULL;
        }
        std::cout << "Destructor called" << std::endl;
    }

    Container(const Container & obj) {
        //Allocate an array of 20 int on heap
        m_Data = new int[20];

        //Copy the data from passed object
        for (int i = 0; i < 20; i++)
            m_Data[i] = obj.m_Data[i];

        std::cout << "Copy Constructor: Allocation 20 int" << std::endl;
    }

    // will give error on adding
    Container(Container&&) = delete;

};

// Create am object of Container and return
Container getContainer() 
{
    Container obj;
    return obj;
}

int main() {
    // Create a vector of Container Type
    std::vector<Container> vecOfContainers;

    //Add object returned by function into the vector
    vecOfContainers.push_back(getContainer());

    return 0;
}

I have written above code just as practice to verify that copy constructor is called when object is returned from getContainer() even though it is an rvalue.

I know that if user defined copy constructor exists, then the compiler will not declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&). Still i decided to add the line "Container(Container&&) = delete;" and get the below error during compilation::

moveconstructorIntro.cpp: In function ‘Container getContainer()’:
moveconstructorIntro.cpp:44:12: error: use of deleted function 
‘Container::Container(Container&&)’
     return obj;
            ^
moveconstructorIntro.cpp:36:5: error: declared here
     Container(Container&&) = delete;
     ^
In file included from /usr/include/c++/4.8.2/x86_64-redhat-
linux/bits/c++allocator.h:33:0,
                 from /usr/include/c++/4.8.2/bits/allocator.h:46,
                 from /usr/include/c++/4.8.2/string:41,
                 from /usr/include/c++/4.8.2/bits/locale_classes.h:40,
                 from /usr/include/c++/4.8.2/bits/ios_base.h:41,
                 from /usr/include/c++/4.8.2/ios:42,
                 from /usr/include/c++/4.8.2/ostream:38,
                 from /usr/include/c++/4.8.2/iostream:39,
                 from moveconstructorIntro.cpp:5:
/usr/include/c++/4.8.2/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = Container; _Args = {Container}; _Tp = Container]’:
/usr/include/c++/4.8.2/bits/alloc_traits.h:254:4:   required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = Container; _Args = {Container}; _Alloc = std::allocator<Container>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.8.2/bits/alloc_traits.h:393:57:   required from ‘static decltype (_S_construct(__a, __p, (forward<_Args>(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = Container; _Args = {Container}; _Alloc = std::allocator<Container>; decltype (_S_construct(__a, __p, (forward<_Args>(std::allocator_traits::construct::__args)...)) = <type error>]’
/usr/include/c++/4.8.2/bits/vector.tcc:97:40:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {Container}; _Tp = Container; _Alloc = std::allocator<Container>]’
/usr/include/c++/4.8.2/bits/stl_vector.h:920:36:   required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = Container; _Alloc = std::allocator<Container>; std::vector<_Tp, _Alloc>::value_type = Container]’
moveconstructorIntro.cpp:52:45:   required from here
/usr/include/c++/4.8.2/ext/new_allocator.h:120:4: error: use of deleted 
function ‘Container::Container(Container&&)’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^
moveconstructorIntro.cpp:36:5: error: declared here
     Container(Container&&) = delete;
     ^

what was the reason for the error?

Please don't try to find any use case for what I am doing over here. I just started writing code in C++ and trying to get a grasp.

6
  • 'delete' is a reserved word in C++. You have a syntax error Commented Mar 21, 2018 at 14:08
  • 1
    @RobertKock - It is reserved. And it's also used correctly here. Despite the unfortunate consequences of using it. Commented Mar 21, 2018 at 14:09
  • 1
    Return local variable will invoke move constructor, which is deleted. Commented Mar 21, 2018 at 14:10
  • OK. I missed something apparently. Haven't been using C++ for a while Commented Mar 21, 2018 at 14:10
  • @SergeyA: 2017? Check your tzdata file. Commented Mar 21, 2018 at 14:11

1 Answer 1

5

When you explicitly deleted the move constructor, you made it available for overload resolution.

Than, when returning local argument by value, move constructor was selected as a better overload candidate (over copy constructor), but could not be used as it is deleted.

Keep in mind, not declaring move constructor and explicitly deleting it yields a different result in regards to the overload resolution process!

EDIT

While what I was saying above is true, as it happens, the particular report reported by compiler in this code is in the other area, namely, a move attempt happening when the value is pushed back into the container. Same thing happens there - a move constructor is selected during overload resolution, but than can not be used as it is deleted.

Sign up to request clarification or add additional context in comments.

19 Comments

"move constructor was selected as a better overload candidate (over copy constructor)" Why? The local variable is still an lvalue, isn't it?
@songyuanyao, there is a special rule. Returning from local parameter or local variable is a candidate for move constructor when available. This is why you should not do return std::move(ret) in your return statements.
@songyuanyao - Specifically, this rule.
Clang said the error comes from vecOfContainers.push_back(getContainer());, because the rvalue returned by getContainer() can't be moved into the vector.
@StoryTeller "If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue." Two-stage overload resolution means, if the move constructor fails, then copy constructor should be selected instead, isn't it?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.