CodeNebula
C++17stladvanced

std::invoke_result

C++17 · Practical Modern · Compiler flag: -std=c++17

Explanation

A type trait that deduces the return type of invoking a callable with given argument types. It replaces the deprecated `std::result_of` with a cleaner interface that does not require wrapping the callable in a function signature.

Real-World Scenario

Deduce the return type of a user-supplied transformation function in a pipeline builder.

Quick Snippet

snippet.cpp
#include <type_traits>

auto square = [](int x) { return x * x; };
using ResultType = std::invoke_result_t<decltype(square), int>;
static_assert(std::is_same_v<ResultType, int>);

Full Example

invoke_result.cpp
// CodeNebula - C++17 Example: std::invoke_result
// Compile: g++ -std=c++17 -Wall -Wextra -o example invoke_result.cpp

// Demonstrates std::invoke_result, the C++17 replacement for the deprecated
// std::result_of. It deduces the return type of invoking a callable with
// given argument types, and is SFINAE-friendly (no hard error on failure).

#include <iostream>
#include <type_traits>
#include <string>
#include <functional>

int add(int a, int b) { return a + b; }
double half(double x) { return x / 2.0; }

struct Converter {
    std::string operator()(int n) const { return std::to_string(n); }
    int operator()(const std::string& s) const { return static_cast<int>(s.size()); }
};

struct Widget {
    int value;
    int get() const { return value; }
};

// Helper to print the deduced type name
template <typename T>
constexpr const char* type_name();
template <> constexpr const char* type_name<int>()         { return "int"; }
template <> constexpr const char* type_name<double>()      { return "double"; }
template <> constexpr const char* type_name<std::string>() { return "std::string"; }

int main()
{
    // ------------------------------------------------------------------
    // 1. Deducing return types of free functions
    // ------------------------------------------------------------------
    using AddResult  = std::invoke_result_t<decltype(add), int, int>;
    using HalfResult = std::invoke_result_t<decltype(half), double>;

    std::cout << "=== Free Functions ===" << std::endl;
    std::cout << "  add(int,int)  -> " << type_name<AddResult>() << std::endl;
    std::cout << "  half(double)  -> " << type_name<HalfResult>() << std::endl;

    // ------------------------------------------------------------------
    // 2. Overloaded function object
    // ------------------------------------------------------------------
    using ConvIntResult = std::invoke_result_t<Converter, int>;
    using ConvStrResult = std::invoke_result_t<Converter, std::string>;

    std::cout << "\n=== Overloaded Functor ===" << std::endl;
    std::cout << "  Converter(int)    -> " << type_name<ConvIntResult>() << std::endl;
    std::cout << "  Converter(string) -> " << type_name<ConvStrResult>() << std::endl;

    // ------------------------------------------------------------------
    // 3. Member function and data pointers
    // ------------------------------------------------------------------
    using GetResult = std::invoke_result_t<decltype(&Widget::get), Widget>;
    using ValResult = std::invoke_result_t<decltype(&Widget::value), Widget>;

    std::cout << "\n=== Member Pointers ===" << std::endl;
    std::cout << "  Widget::get()  -> " << type_name<GetResult>() << std::endl;
    std::cout << "  Widget::value  -> " << type_name<ValResult>() << std::endl;

    // ------------------------------------------------------------------
    // 4. Lambda return type deduction
    // ------------------------------------------------------------------
    auto square = [](int x) { return x * x; };
    using SqResult = std::invoke_result_t<decltype(square), int>;

    std::cout << "\n=== Lambda ===" << std::endl;
    std::cout << "  square(int)   -> " << type_name<SqResult>() << std::endl;

    // ------------------------------------------------------------------
    // 5. Practical: generic wrapper using invoke_result_t
    // ------------------------------------------------------------------
    auto call_and_report = [](auto fn, auto... args) {
        using R = std::invoke_result_t<decltype(fn), decltype(args)...>;
        R result = std::invoke(fn, args...);
        std::cout << "  Result = " << result << std::endl;
    };

    std::cout << "\n=== Generic Wrapper ===" << std::endl;
    call_and_report(add, 100, 200);
    call_and_report(half, 9.0);

    return 0;
}

/*
Expected output:

=== Free Functions ===
  add(int,int)  -> int
  half(double)  -> double

=== Overloaded Functor ===
  Converter(int)    -> std::string
  Converter(string) -> int

=== Member Pointers ===
  Widget::get()  -> int
  Widget::value  -> int

=== Lambda ===
  square(int)   -> int

=== Generic Wrapper ===
  Result = 300
  Result = 4.5
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example invoke_result.cpp