OpenGL97
OpenGL97

Reputation: 289

ofstream in class - attempting to reference a deleted function

I have a member variable in class which its type is ofstream and a constructor which contains string parameter:

class dogs
{
public:
    ofstream dogsFile;

    dogs(string location)
    {

    }
};

The following error appears:

Error 2 error C2280: 'std::basic_ofstream>::basic_ofstream(const std::basic_ofstream> &)' : attempting to reference a deleted function c:\users\pc\documents\visual studio 2013\projects\database\database\database.cpp 26 1 Database

I have tried this code again but instead of using string I used char*:

class dogs
{
public:
    ofstream dogsFile;

    dogs(char* location)
    {

    }
};

And the error disappeared. Why? why does string makes the error?

Edit: This is the whole code:

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;


class dogs
{ 
    ofstream dogsFile;

public:
    dogs(string location)
    {

    }
};

int main(int argc, _TCHAR* argv[])
{
    dogs dog = dogs("dog.bin");
    return 1;
}

Upvotes: 6

Views: 2652

Answers (2)

bogdan
bogdan

Reputation: 9317

I can't think of this as anything else than a bug in VC++.

The way the dogs class is defined, the compiler should generate an implicit move constructor for it, which will call the move constructor for std::ofstream, which is defined as of C++11.

The rules for when to define an implicit move constructor are specified in the standard in [12.8/9] and [12.8/11]. They're also listed here. I can't see any reason not to declare the implicit move constructor in this case.

Then, the line

dogs dog = dogs("dog.bin");

must invoke a move from the temporary dogs object on the right hand side to the left-hand-side dog (the move may be elided, but the constructor needs to be accessible nonetheless).

In short, everything should work fine.

And it does in Clang 3.5. It doesn't work in GCC, but only because it doesn't have move constructors defined for stream classes (it's not standard-compliant in that respect).

Funnily enough, it works in VC++, as the OP duly mentioned, if the dogs constructor is declared to take anything else but a std::string by value. Change the constructor to any of the following:

dogs(const string& location)

or

dogs(int a) //And change the call accordingly, of course.

or

dogs(A a) //And change the call accordingly (A is a user-defined class).

and everything will work fine in VC++, as it should.

The fact that it doesn't work only for std::string passed by value seems to indicate there's a bug in the compiler. Basically, in that case, and only in that case, the compiler decides not to define the implicit move constructor, which causes the copy initialization to fall back to calling the copy constructor, which causes the error.

If you explicitly define the move constructor, like this:

dogs(dogs&& arg) : dogsFile(std::move(arg.dogsFile)) { }

again, everything works fine, including with the constructor taking a std::string by value.

I can't see any valid reason for VC++ to behave this way.

Upvotes: 0

Nicko Po
Nicko Po

Reputation: 727

The original answer by Dieter seems to be correct. I.e. This will compile:

dogs *dog = new dogs("dog.bin");

Your line will not, see his answer about copy constructors.

the dogs("dog.bin") will create an object then "=" will make a copy of it and give it to dog. Can't copy object with ofstream in it.

You can also fix this by using

dogs dog("dog.bin");

instead.

Upvotes: 4

Related Questions