Unique pointer in base class prohibts instantiation with error "attempting to reference a deleted function"

Viewed 257

I updated my C++ toolchain from Visual Studio 2013 to Visual Studio 2017/2019.

Now I am experiencing a number of compile errors in the form:

<source>(13): error C2280: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': attempting to reference a deleted function

<source>(8): note: compiler has generated 'OfflineFixture::OfflineFixture' here

<source>(8): note: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)'

A unique pointer is a member of class that has no constructor and destructor. In this case Visual Studio allows to instantiate an object this way:

OfflineFixture a{};  // works!

But using:

auto&& a = OfflineFixture{};

gives above compile error.

const auto& a = OfflineFixture{};

also gives above compile error.

Please have a look here: https://gcc.godbolt.org/z/XtP40t

My question is: Is my code wrong?

The given examples compiles using: gcc (9.1 and lower) clang Visual Studio 2013

But it fails in:

  • Visual Studio 2015

  • Visual Studio 2017

  • Visual Studio 2019

One way to fix this is to implement a default constructor in OfflineFixture.

Minimal example:

#include <memory>

struct OfflineFixture
{
    void x() const {} 
    int i;
    std::unique_ptr<int> m_calc;
};

int test() {

#if 1
    const auto&& a = OfflineFixture{};
#else
    OfflineFixture a{};
#endif
    a.x();

    return 0;
}
3 Answers

Let's make some preliminary points here.

In general, the statement:

const auto&& a = Foo{};

is perfectly legal in C++. Moreover, it is not true that it is undefined behavior. In fact, this is a perfect example of Reference Initialization with lifetime extension of a temporary.

Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference [...].

It continues with some exceptions (I won't quote all of them), but the declaration of a new object is not part of those exceptions.


Regarding your class, it appears to be clear the problem is the std::unique_ptr member.

Here a minimal example:

#include <memory>

struct OfflineFixture {
  std::unique_ptr<int> m_calc;
};

void test() {
  const auto&& a = OfflineFixture{};  // <--- Error in msvc
}

Somehow, MSVC tries to create a copy of the object. Indeed, the error is about invoking the copy constructor (which is deleted because of std::unique_ptr).

Since no copies should be performed here, it appears to be a msvc bug.

Note: gcc and clang compile fine with that.

I have added constructor and move constructor as shown in below code.

This resolves the issue.

struct OfflineFixture
{
    void x() const {} 
    int i;
    std::unique_ptr<int> m_calc;

    OfflineFixture(){}
    OfflineFixture(OfflineFixture &&){}

    //implicit
    OfflineFixture(const OfflineFixture&) = default;
    OfflineFixture& operator=(const OfflineFixture&) = default;
    ~OfflineFixture() = default;

 };

As, const auto&& a = OfflineFixture{}; requires constructor and move constructor

I hope it helps!

It is interesting to note that MSVC shows this erroneous behavior only if you do not provide a constructor or if you provide a default one:

struct OfflineFixture {
    OfflineFixture()=default;
    //...
};

The example can be fixed by providing a constructor with an empty block:

struct OfflineFixture {
    OfflineFixture(){}
    //...
};

which compiles just fine.

Related