10

Let's say I have a class

class Object
{
public:
    Object(int i) : num(i) {};

    int getNum() const { return num; }
private:
    int num;
};

Now, if I try to do this

Object obj{ 1 };
Object obj2{ 2 };
Object obj3 = obj + obj2; //This is wrong

This is illegal: 'Object' does not define this operator or a conversion to a type acceptable to the predefined operator.

Adding Object operator+(const Object&) = delete; doesn't really change anything, except for the error message: 'Object Object::operator +(const Object &)': attempting to reference a deleted function.

Is the delete only needed for operators that have an implicit declaration (like assignment operator and copy/move constructor) or does it change anything else in my case?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162

2 Answers2

8

Is the delete only needed for operators that have an implicit declaration (like assignment operator and copy/move constructor) or does it change anything else in my case?

No and no. Your case is merely too simple for any such difference to matter.

= delete serves two purposes:

1: It forcibly removes functions which would (potentially) otherwise be there. Namely, the special member functions.

2: It specifies that a function with that signature cannot be called. This is essentially a generalization of #1.

The archetypal example of the latter is for preventing implicit conversion of function arguments. If you do this:

void foo(float val);
void foo(int) = delete;

You can no longer call foo(5); you must use foo(5.f). Why? Because the compiler will see the second overload, see that it matches the call the best, and then immediately fail because the function is deleted.

This is sometimes done like this as well:

void foo(float val);
template<typename T> void foo(T&&) = delete;

This ensures that you can only call it with an actual float. No type which is implicitly convertible from float will work.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Why is the rvalue reference needed for the template, in the last case? It would seem to me that just a plain "templatevoid foo(T)=delete" would be sufficient for that purpose. – Sam Varshavchik Mar 27 '16 at 02:49
  • @SamVarshavchik: It's probably not necessary. – Nicol Bolas Mar 27 '16 at 02:49
  • 1
    @SamVarshavchik Consider, e.g., `void g(AbstractClassWithConversionToFloat& x) { foo(x); }`. `void foo(T) = delete;` would have a substitution failure (because function parameter types cannot be abstract) and incorrectly fail to block the call. – T.C. Mar 27 '16 at 04:16
1

The anwer by @NicolBolas is perfectly acceptable. Here's another example to illustrate that sometimes it's not enough not to declare/define a function because someone else does (such as the compiler generated special member functions) and your code can call it (e.g. through implicit constructors or conversion operators).

#include <iostream>

// your code
struct Base {};

// some teammate
struct Wrap
{
    /* implicit */ Wrap(Base const& b) {}
};

void fun(Wrap const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

// client code
int main()
{
    Base b;
    fun(b); // oops, probably didn't intend to call fun(Wrap const&)
}

Live Example that outputs void fun(const Wrap &).

Now in the old days pre-C++11, you could declare but not define

void fun(Base const&); // no definition provided

Live Example that outputs the linker error

main.cpp:(.text+0x36): undefined reference to 'fun(Base const&)'

From C++11 onwards, you can explicitly delete the same function

void fun(Base const&) = delete; // C++11 onwards

Live Example that outputs the compiler error (which is much more informative than the previous linker error)

main.cpp:20:5: error: call to deleted function 'fun'
    fun(b); // oops, probably didn't intend to call fun(Wrap const&)
    ^~~
main.cpp:6:6: note: candidate function has been explicitly deleted
void fun(Base const&) = delete; // C++11 onwards
     ^
TemplateRex
  • 69,038
  • 19
  • 164
  • 304