Meaning of = Delete After Function Declaration

Meaning of = delete after function declaration

Deleting a function is a C++11 feature:

The common idiom of "prohibiting copying" can now be expressed
directly:

class X {
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};

[...]

The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:

struct Z {
// ...

Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything less
};

delete modifier vs declaring function as private

One difference is that =delete allows for compile-time errors while in some cases the declaration without a definition is only caught at link-time (at which the error message is typically not pointing you to the source of the problem). One such case is when you add a member function that tries to copy an instance of A. Even when it's not a member function of A, the error message about the copy-ctor being private is not as clear as using =delete.

To avoid confusion, I'd recommend you make the deleted function public as otherwise you will get additional and misleading error messages.

What does default mean after a class' function declaration?

It's a new C++11 feature.

It means that you want to use the compiler-generated version of that function, so you don't need to specify a body.

You can also use = delete to specify that you don't want the compiler to generate that function automatically.

With the introduction of move constructors and move assignment operators, the rules for when automatic versions of constructors, destructors and assignment operators are generated has become quite complex. Using = default and = delete makes things easier as you don't need to remember the rules: you just say what you want to happen.

What's the exact semantics of deleted member functions in C++11?

a = A(); // error C2280

The expression on the right is a temporary which means it will look for operator=(A&&) and sees it is deleted. Hence the error. There is no further search.

=delete does not mean "don't use me, instead use next best one". It rather means, "don't use me when you need me — instead be alone in the wild."

Here is another example. If I want the instances of my class X to be created with only long and no other type (even if it converts into long!), then I would declare class X as:

struct X
{
X(long arg); //ONLY long - NO int, short, char, double, etc!

template<typename T>
X(T) = delete;
};

X a(1); //error - 1 is int
X b(1L); //ok - 1L is long

That means, the overload resolution is performed before the compiler sees the =delete part — and thus results in an error because the selected overload is found deleted.

Hope that helps.

Warning when an explicitly defaulted function declaration is deleted

What you need to do is move the definition of the default member out of the class:

class NotMoveA
{
public:
NotMoveA() = default;
~NotMoveA() = default;
NotMoveA(const NotMoveA &) = delete;
NotMoveA(NotMoveA &&other) = default;
NotMoveA &operator=(const NotMoveA &) = delete;
//will B deleted w/o warning:
NotMoveA &operator=(NotMoveA &&other);
// ...
private:
const std::string badDataMemberDisallowingMoveAssignment;
// ...
};

NotMoveA & NotMoveA::operator=(NotMoveA &&other) = default;

Once you make it an out of line definition then you will get a compiler error as you cannot define the member function via = default if it would be deleted:

error: defaulting this move assignment operator would delete it after
its first
declaration NotMoveA & NotMoveA::operator=(NotMoveA &&other) = default;

Declaring a function as defaulted after its first declaration

Suppose you have

// A.h
struct A {
A();
};

and

// A.cc
A::A() { }

You can change it to

// A.cc
A::A() = default;

to not force code using A.h to be recompiled.

For a default constructor, this doesn't make much sense. = default takes up more characters than { }. But think of other constructor types: a copy or move constructor may become much shorter if it is no longer necessary to explicitly mention each field, and depending on the compiler and type you're dealing with, the defaulted copy/move constructor may even perform better, for example if the compiler can only detect that a memcpy call will suffice when you use the = default syntax.

error: use of deleted function

The error message clearly says that the default constructor has been deleted implicitly. It even says why: the class contains a non-static, const variable, which would not be initialized by the default ctor.

class X {
const int x;
};

Since X::x is const, it must be initialized -- but a default ctor wouldn't normally initialize it (because it's a POD type). Therefore, to get a default ctor, you need to define one yourself (and it must initialize x). You can get the same kind of situation with a member that's a reference:

class X { 
whatever &x;
};

It's probably worth noting that both of these will also disable implicit creation of an assignment operator as well, for essentially the same reason. The implicit assignment operator normally does members-wise assignment, but with a const member or reference member, it can't do that because the member can't be assigned. To make assignment work, you need to write your own assignment operator.

This is why a const member should typically be static -- when you do an assignment, you can't assign the const member anyway. In a typical case all your instances are going to have the same value so they might as well share access to a single variable instead of having lots of copies of a variable that will all have the same value.

It is possible, of course, to create instances with different values though -- you (for example) pass a value when you create the object, so two different objects can have two different values. If, however, you try to do something like swapping them, the const member will retain its original value instead of being swapped.

Why were parentheses disambiguated as a function declaration with std::istream_iterator?

This line

 deque<string> q(std::istream_iterator<string>(ss),
std::istream_iterator<string>());

is a function declaration with the return type deque<string> and two parameters of the type std::istream_iterator<string>. The first parameter has the name ss and the second parameter is unnamed.

To make this line a declaration of the variable q you should write either

 deque<string> q( ( std::istream_iterator<string>(ss) ),
( std::istream_iterator<string>() ) );

or

 deque<string> q(std::istream_iterator<string>{ ss },
std::istream_iterator<string>{});

In this case there are used expressions instead of parameter declarations.

You may include declarators in parentheses. For example

int ( x );

When a declaration is a parameter declaration then you may omit a declarator like

int()

Here is an example of three declarations of the same function.

int f( int( x ) );
int f( int x );
int f( int( x ) )
{
return 2 * x;
}


Related Topics



Leave a reply



Submit