Preventing Unwanted Function Calls

Sometimes you want to prevent a function from being called with certain argument types. While the compiler allows implicit conversions that might not make sense, you can take control and forbid specific uses.

Consider this example:

#include <iostream>

void showValue(int number)
{
    std::cout << number << '\n';
}

int main()
{
    showValue(42);      // makes sense: prints 42
    showValue('z');     // prints 122 (ASCII value) -- is this what we want?
    showValue(false);   // prints 0 -- is this what we want?

    return 0;
}

This outputs:

42
122
0

The first call works as expected. However, calling showValue('z') promotes the character to its integer representation (122), and showValue(false) converts the boolean to 0. These conversions happen silently, potentially masking bugs.

If you decide that calling showValue() with characters or booleans doesn't make sense, what can you do?

Using = delete to Prevent Function Calls

The = delete specifier marks a function as deleted. If the compiler selects a deleted function during overload resolution, it generates a compilation error:

#include <iostream>

void showValue(int number)
{
    std::cout << number << '\n';
}

void showValue(char) = delete;    // forbid char arguments
void showValue(bool) = delete;    // forbid bool arguments

int main()
{
    showValue(42);      // OK: prints 42

    showValue('z');     // Compile error: function is deleted
    showValue(false);   // Compile error: function is deleted

    showValue(8.5);     // Compile error: ambiguous match

    return 0;
}

Let's examine what happens with each call:

  • showValue('z'): Exact match for showValue(char), which is deleted. Compilation fails.
  • showValue(false): Exact match for showValue(bool), which is deleted. Compilation fails.
  • showValue(8.5): The compiler looks for showValue(double) but doesn't find it. Then it searches for the best match among all functions, including deleted ones. Since multiple functions could match equally well (through different conversions), the call is ambiguous.
Key Concept
Deleted functions with = delete mean "this is forbidden," not "this doesn't exist." Deleted functions participate fully in overload resolution. If a deleted function is chosen as the best match, compilation stops with an error.

Deleting All Non-Matching Types with Templates

Manually deleting individual overloads works but becomes tedious when you want to accept only one specific type. Function templates offer a more concise solution:

#include <iostream>

void showValue(int number)
{
    std::cout << number << '\n';
}

template <typename T>
void showValue(T) = delete;  // delete all other types

int main()
{
    showValue(42);      // OK: exact match with showValue(int)
    showValue('z');     // Compile error: matches deleted template
    showValue(false);   // Compile error: matches deleted template
    showValue(3.14);    // Compile error: matches deleted template

    return 0;
}

With this approach, the non-template showValue(int) function takes priority for int arguments. For any other type, the template version matches—and since it's deleted, compilation fails. This technique ensures that only exact int arguments are accepted, preventing any implicit conversions.

Best Practice
Use = delete to explicitly forbid function calls that could compile but don't make semantic sense for your use case.

Summary

The = delete specifier gives you control over which function calls are allowed:

Key Concepts:

  • Deleted functions are marked with = delete after the parameter list
  • Deleted functions participate in overload resolution but cause compilation errors if selected
  • This means "forbidden" not "doesn't exist"

Use Cases:

  • Prevent implicit type conversions that don't make sense
  • Restrict functions to specific types only
  • Catch programming errors at compile time

Template Technique:

  • Use a deleted function template to forbid all types except specific overloads
  • Non-template functions take priority over templates for exact matches
  • Provides a concise way to accept only one specific type