Prefer deleted functions to private undefined ones
This item (11) in the chapter 3 focuses on:
- Why and How to prevent users calling particular functions?
- C++-98 and C++-11 approach
- What’s the difference between deleting a function vs declaring a member function private (and not defining them)?

NOTE
These are my notes on Chapter 3, Item 11 of Effective Modern C++ written by Scott Meyers.
Some (or even all) of the text can be similar to what you see in the book, as these are notes: I’ve tried not to be unnecessarily creative with my words. :)
- When is it required to delete a function/not define a private member function?
Problem:
- Cases when you don’t want the client to call a particular function.
Solution:
- Just don’t declare the function
But…doesn’t work always:
Case: Special member functions generated by C++ automatically, discussed later). Examples considered in this blog:
- Copy Constructor
- Copy Assignment Operator
C++-98 Approach:
Declare these functions private and don’t define them.
Example:
- All
istreamandostreamobjects inherit (possibly) frombasic_iosclass in the C++ Standard Library. - Copying these objects is undersirable.
- All
Why is copying objects of
istreamandostreamundesirable? [Also see this question on stackoverflow]istreamobject: represents stream of input values.- some might have been read before.
- and some may be read later.
- If you copy
istreamobject:- Will that copy those values which have been read before?
- Or will also copy values which are to be read later?
Hence, it’s just better to not allow copying istream or ostream objects.
In C++-98:
Reminder (from above): All istream and ostream objects inherit from basic_ios class (possibly) in the C++ standard, and the basic_ios class in C++-98 looks something like this:
template <class charT, class traits = char_traits<T> >
class basic_ios : public ios_base {
public:
// ...
// Declaring the copy constructor and copy assignment operator private prohibits clients from calling them
private:
basic_ios(const basic_ios&); // not defined
basic_ios& operator=(const basic_ios&); // not defined
};
Note that:
basic_ios(const basic_ios&)is the copy constructorbasic_ios& operator=(const basic_ios&)is the copy assignment operator
(and both are private).
How does not defining these functions help?
- Consider a case where a
friendclass or member functions try accessing these functions, then linking will fail because of missing function definitions.
In C++-11
In C++-11, the above can be done using = delete to mark the copy constructor and the copy assignment operator as deleted functions.
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
// ...
basic_ios(const basic_ios& ) = delete; // deleted function
basic_ios& operator=(const basic_ios&) = delete; // deleted function
// ...
};
It’s a convention to declare deleted functions public, but why? Better error messages.
In case you declare your deleted functions private, some compilers will probably complain about the function being private and can hide the error message of it not being usable (because it being deleted). Hence, it’s a good practice to make them public:
From my experience though, Apple’s clang compiler (v 12.0.5) doesn’t complain about it being private, but then - it can vary from compiler to compiler, so better to play safe. Here is an example of how the error message looks like:
#include <iostream>
class Sample {
public:
Sample(int x) : x(x) { };
// Copy constructor has been deleted, so should not be callable
Sample(const Sample&) = delete;
void trying_copy_construct(Sample s) {
// This function tries to use a copy constructor
// This should fail
Sample new_object(s);
}
private:
int x;
};
Compiling the above code fails with the following error:
main.cpp:10:16: error: call to deleted constructor of 'Sample'
Sample new_object(s);
^ ~
main.cpp:6:5: note: 'Sample' has been explicitly marked deleted here
Sample(const Sample&) = delete;
Difference b/w using delete vs declaring private
Note: There is more to it except the good practice reasoning.
- Using a deleted function in a member function or by a
friendclass won’t even compile the code if it tries to copybasic_iosobjects while declaringprivatewill compile successfully but fail during link-time. - Conventionally deleted functions are declared
public, notprivatewhile the C++-98 way requires the functions to be declared private (see the section above for reasoning). - Only member functions can be private, while any function can be deleted (so you can delete some overloads for your function, in case you don’t want it to accept certain type inputs).
Let’s discuss the last point in detail:
Consider the case where you have:
template<typename T>
void processPointer(T* ptr);
And:
- You need a template that works with built-in pointers. You want to reject calls with
void*andchar*pointers (more on this later, these deserve special handling at times). - Ideal way? Delete these instantiations (with
void*orchar*input pointers).
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
But, with C++-98 way, it’s not possible within the class scope. That is, you can not give template specialization to your member function within the class scope (it should be done in the namespace scope):
class Sample {
public:
// ...
template <typename T>
void processPointer(T* ptr)
{ ... }
private:
// ...
// This is template specialization inside the scope of the class - not allowed
template <>
void processPointer<void>(void*); // error
};
While with the C++-11 way, you can delete function outside the class scope:
class Sample {
public:
// ...
template <typename T>
void processPointer(T* ptr)
{ ... }
// ...
};
template <>
void Sample::processPointer<void>(void*) = delete; // in public scope, and deleted!
I hope you liked this blog, thank you for reading! :)