02 Overloading, Default Arguments & Namespaces

Overview: Scope and Signature Resolution

C++ resolves function calls and variable scopes at compile time using strict structural signatures. This note explores how the compiler distinguishes functions with identical names, evaluates parameters, and manages namespaces.


1. Function Overloading & Name Mangling

Function Overloading allows multiple functions in the same scope to share the same name, provided they have different parameter list signatures.

void print(const char* str);
void print(int val);

Under the Hood: Name Mangling

In standard C, symbols are compiled exactly as written (e.g., print becomes the linker symbol _print). Because linkers cannot support duplicate symbols, C does not support overloading.

C++ uses Name Mangling. The compiler translates function names into unique linker symbols containing type details. For example:

  • void print(const char*) mangles to _Z5printPKc
  • void print(int) mangles to _Z5printi

The linker sees two entirely distinct functions, enabling polymorphism.


2. Default Function Arguments

You can specify default values for parameters if the caller omits them.

void setupDevice(int port, int baudRate = 9600, bool enableFIFO = true);

Mechanics: Where do they reside?

Default arguments are evaluated and inserted by the compiler at the call site, not inside the function itself.

When you write setupDevice(5), the compiler rewrites the instruction at compile time to:

setupDevice(5, 9600, true);

The Right-to-Left Rule:

Default arguments must be declared from right-to-left. Once a parameter is given a default value, all subsequent parameters to its right must also have defaults.

// INVALID: compiler cannot resolve which argument is omitted
void invalidFunc(int port = 80, int baudRate); 

3. Namespaces and Scope Resolution

Namespaces prevent symbol collisions by partitioning the global scope.

namespace First {
    void foo() { std::printf("First\n"); }
}
 
namespace Second {
    void foo() { std::printf("Second\n"); }
}

Scope Resolution Operators:

  • Explicit Selection (::): Direct selection using the namespace name, e.g., First::foo().
  • The Global Namespace (::): Bypasses all active scopes to call a global variable or function, e.g., ::foo().
  • The Using Directive (using namespace Second;): Pulls all symbols from a namespace into the current block.

    Namespace Pollution using namespace in header files. If you do, every source file importing that header will have its namespace polluted, potentially causing naming conflicts.

    Avoid placing


💻 Conceptual Code Demonstration

#include <cstdio>
 
// 1. Namespace declarations
namespace Audio {
    void init(int sampleRate = 44100) { // Default argument
        std::printf("Audio Initialized at %d Hz\n", sampleRate);
    }
}
 
namespace Video {
    // Overloaded function within a different namespace
    void init(int width, int height) {
        std::printf("Video Initialized at %dx%d resolution\n", width, height);
    }
}
 
// Global function (resides in global scope)
void init() {
    std::printf("Global init called!\n");
}
 
int main() {
    // 2. Call-site parameter mapping
    Audio::init();        // Compiler inserts 44100 -> Audio::init(44100)
    Audio::init(96000);   // Overrides default
    
    Video::init(1920, 1080);
    
    // 3. Resolving scope conflicts
    using namespace Audio;
    // init(); // ERROR: Ambiguous call! Audio::init or Global init?
    
    ::init(); // Resolves unambiguously to global scope init()
    
    return 0;
}