'(declared implicitly) cannot be referenced -- it is a deleted function

With the following code, I'm facing an issue.

ABC.h

namespace abcd
{
    class ABC
    {
    public:
        ABC() = delete;
        ABC(const std::string& filename);
        virtual ~ABC();
        ABC(const ABC&) = delete;
        ABC(ABC&&) = default;
    };
}

XYZ.h

using namespace abcd;

class XYZ
{
public:
    void func();
private:
    ABC obj;
    ABC maskfilename(std::string filename);
};

XYZ.cpp

XYZ::func()
{
    obj = maskfilename("abcd.txt"); //Error
}

abcd::ABC XYZ::maskfilename(string filename)
{
    abcd::ABC ret;
    // blah blah...
    return ret;
}

Error:

"abcd::ABC::operator=(const abcd::ABC &)" (declared implicitly) cannot be referenced -- it is a deleted function

I understand it is move constructor only class (ABC).

What is the right way to use this? I want to retain the return value from maskfilename() in the XYZ class, so it can be used in other functions of the XYZ class.

How can I resolve this error?



Solution 1:[1]

This is the declaration of class ABC:

namespace abcd
{
    class ABC
    {
    public:
        ABC() = delete;
        ABC(const std::string& filename);
        virtual ~ABC();
        ABC(const ABC&) = delete;
        ABC(ABC&&) = default;
    };
}

If you used a Microsoft compiler, you would get MSVC error C2280:

The compiler detected an attempt to reference a deleted function. This error can be caused by a call to a member function that has been explicitly marked as = deleted in the source code. This error can also be caused by a call to an implicit special member function of a struct or class that is automatically declared and marked as deleted by the compiler. For more information about when the compiler automatically generates default or deleted special member functions, see Special member functions.

In your case, apart from the conversion constructor, your class ABC only has a move constructor. Since you declared a move constructor, the compiler implicitly deleted the copy-assignment operator. That's why you're getting this error. This line is trying to invoke your deleted copy assignment operator:

obj = maskfilename("abcd.txt"); //Error

You can read more here:

  • If any constructor is explicitly declared, then no default constructor is automatically generated.
  • If a virtual destructor is explicitly declared, then no default destructor is automatically generated.
  • If a move constructor or move-assignment operator is explicitly declared, then:
    • No copy constructor is automatically generated.
    • No copy-assignment operator is automatically generated.
  • If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then:
    • No move constructor is automatically generated.
    • No move-assignment operator is automatically generated.

Additionally, the C++11 standard specifies the following additional rules:

  • If a copy constructor or destructor is explicitly declared, then automatic generation of the copy-assignment operator is deprecated.
  • If a copy-assignment operator or destructor is explicitly declared, then automatic generation of the copy constructor is deprecated.

Since you want your class to be a move-only class, just declare a move assignment operator and then the same line will invoke your move assignment operator. Then the rvalue that maskfilename returns will be moved into obj.

Here's a reworked implementation of your class ABC:

class ABC
{
public:
    ABC() = delete;
    ABC(std::string filename) : mFilename(std::move(filename)) {}
    virtual ~ABC() = default;
    ABC(const ABC&) = delete;
    ABC& operator= (const ABC&) = delete;
    ABC(ABC&&) = default;
    ABC& operator= (ABC&&) = default;
    std::string GetFilename() const { return mFilename; }

private:
    std::string mFilename;
};

In your class XYZ you can't have a member variable obj of type ABC since the default constructor is deleted. If you need to use such an object in other methods of XYZ you could have a unique pointer to ABC instead. So I would change that like this:

XYZ.h:

#include "ABC.h"
#include <memory>

class XYZ
{
public:
    void func();

private:
    std::unique_ptr<ABC> obj;
    std::unique_ptr<ABC> maskfilename(std::string filename);
};

XYZ.cpp:

void XYZ::func()
{
    obj = std::move(maskfilename("abcd.txt"));
}

std::unique_ptr<ABC> XYZ::maskfilename(std::string filename)
{
    std::unique_ptr<ABC> ret = std::make_unique<ABC>(filename);
    // blah blah...
    return ret;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1