CodeNebula
C++11coreintermediate

Trailing Return Types (C++11 Addition)

C++11 · Modern Revolution · Compiler flag: -std=c++11

Explanation

A syntax using `auto` and `->` to specify the return type after the parameter list, essential when the return type depends on parameter types in template functions.

Real-World Scenario

Write a generic add function where the return type depends on the types of the operands.

Quick Snippet

snippet.cpp
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

Full Example

trailing_return_types.cpp
// CodeNebula - C++11 Example: Trailing Return Types
// Compile: g++ -std=c++11 -Wall -Wextra -o example trailing_return_types.cpp
//
// This example demonstrates:
// - The auto f() -> ReturnType syntax introduced in C++11
// - Trailing return types with decltype for deduced return types
// - Use cases where trailing return types simplify template code
// - Combining trailing return types with lambdas

#include <iostream>
#include <string>
#include <vector>

// ============================================================
// 1. Basic trailing return type syntax
// ============================================================
auto add(int a, int b) -> int {
    return a + b;
}

auto greet(const std::string& name) -> std::string {
    return "Hello, " + name + "!";
}

// ============================================================
// 2. Trailing return type with decltype (deduced return)
// ============================================================
template <typename T, typename U>
auto multiply(T a, U b) -> decltype(a * b) {
    return a * b;
}

// Without trailing return type, you cannot refer to parameters
// in the return type position. This solves that problem.

// ============================================================
// 3. Trailing return type depending on class member
// ============================================================
class Container {
    std::vector<double> data_;
public:
    Container(std::initializer_list<double> vals) : data_(vals) {}

    auto size() const -> std::vector<double>::size_type {
        return data_.size();
    }

    auto front() const -> const double& {
        return data_.front();
    }

    auto begin() -> std::vector<double>::iterator { return data_.begin(); }
    auto end() -> std::vector<double>::iterator { return data_.end(); }
};

// ============================================================
// 4. Function pointer with trailing return type
// ============================================================
auto getOperation(char op) -> int(*)(int, int) {
    switch (op) {
        case '+': return [](int a, int b) -> int { return a + b; };
        case '-': return [](int a, int b) -> int { return a - b; };
        default:  return [](int a, int b) -> int { return a * b; };
    }
}

int main() {
    std::cout << "=== Trailing Return Types ===" << std::endl;

    // Basic usage
    std::cout << "add(3, 4) = " << add(3, 4) << std::endl;
    std::cout << "greet(\"C++11\") = " << greet("C++11") << std::endl;

    // Template with decltype
    std::cout << "multiply(3, 4.5) = " << multiply(3, 4.5) << std::endl;
    std::cout << "multiply(2.0f, 3) = " << multiply(2.0f, 3) << std::endl;

    // Class members
    Container c{1.1, 2.2, 3.3};
    std::cout << "Container size: " << c.size() << std::endl;
    std::cout << "Container front: " << c.front() << std::endl;

    // Function pointer
    auto op = getOperation('+');
    std::cout << "getOperation('+')(10, 5) = " << op(10, 5) << std::endl;
    op = getOperation('-');
    std::cout << "getOperation('-')(10, 5) = " << op(10, 5) << std::endl;

    // Lambda with trailing return type
    auto divide = [](double a, double b) -> double {
        return (b != 0.0) ? a / b : 0.0;
    };
    std::cout << "divide(10.0, 3.0) = " << divide(10.0, 3.0) << std::endl;

    return 0;
}

// Expected output:
// === Trailing Return Types ===
// add(3, 4) = 7
// greet("C++11") = Hello, C++11!
// multiply(3, 4.5) = 13.5
// multiply(2.0f, 3) = 6
// Container size: 3
// Container front: 1.1
// getOperation('+')(10, 5) = 15
// getOperation('-')(10, 5) = 5
// divide(10.0, 3.0) = 3.33333
Run command
g++ -std=c++11 -Wall -Wextra -o example trailing_return_types.cpp