std::mem_fn(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::mem_fn(3) β€” C++ Standard Library, bestie edition πŸŽ€

okay so welcome to std::mem_fn(3), straight out of the C++ Standard Library, and no I will not be normal about it ✨


🌸 NAME 🌸

std::mem_fn β€” literally just std::mem_fn, that's the whole name, we're keeping it simple periodt πŸ’–


✨ Synopsis ✨

first things first, it lives here:

Defined in header <functional>

and here's the whole shape of it, watch closely:

template< class M, class T >                               (since C++11)
/* unspecified */ mem_fn( M T::* pm ) noexcept;            (until C++20)

template< class M, class T >                               (since C++20)
constexpr /* unspecified */ mem_fn( M T::* pm ) noexcept;

so the function template std::mem_fn generates wrapper objects for pointers to members β€” which can store, copy, and invoke a pointer to member. Both references and pointers (including smart pointers 🧠) to an object can be used when you're invoking a std::mem_fn. it's giving versatility, it's giving range 🌈


πŸ’œ Parameters πŸ’œ

  • pm β€” pointer to member that will be wrapped 🎁

πŸ¦‹ Return value πŸ¦‹

std::mem_fn returns a call wrapper fn of unspecified type that has the following members (yes, unspecified β€” the vibes are immaculate but the type is a mystery πŸ™ˆ):

std::mem_fn return type

πŸ‘‘ Member types

here's the little table of types, translated with love:

  • result_type (deprecated in C++17) β€” the return type of pm if pm is a pointer to member function, not defined for pointer to member object
  • argument_type (deprecated in C++17) β€” T*, possibly cv-qualified, if pm is a pointer to member function taking no arguments
  • first_argument_type (deprecated in C++17) β€” T* if pm is a pointer to member function taking one argument
  • second_argument_type (deprecated in C++17) β€” T1 if pm is a pointer to member function taking one argument of type T1
these are all until C++20 territory btw, and they're deprecated since C++17 β€” so like, use them if you must but know they're on their way out πŸ’€

πŸ“ Member function

template< class... Args >
/* see below */ operator()(Args&&... args) /* cvref-qualifiers */   (until C++20)
    noexcept(/* see below */);

template< class... Args >
constexpr /* see below */ operator()(Args&&... args) /* cvref-qualifiers */   (since C++20)
    noexcept(/* see below */);

so the expression fn(args) is equivalent to INVOKE(pmd, args), where pmd is the Callable object held by fn β€” it's of type M T::* and is direct-non-list-initialized with pm. yes that's a whole mouthful, I know, but it matters 🫢

thus, the return type of operator() is std::result_of<decltype(pm)(Args&&...)>::type or equivalently std::invoke_result_t<decltype(pm), Args&&...>, and the value in the noexcept specifier is equal to std::is_nothrow_invocable_v<decltype(pm), Args&&...>) (since C++17).

each argument in args is perfectly forwarded, as if by std::forward<Args>(args)... β€” perfect forwarding my beloved πŸ’…


πŸ”₯ Example πŸ”₯

use std::mem_fn to store and execute a member function and a member object β€” here's the whole thing, run it, it's iconic:

// Run this code

#include <functional>
#include <iostream>
#include <memory>

struct Foo
{
    void display_greeting()
    {
        std::cout << "Hello, world.\n";
    }

    void display_number(int i)
    {
        std::cout << "number: " << i << '\n';
    }

    int add_xy(int x, int y)
    {
        return data + x + y;
    }

    template<typename... Args> int add_many(Args... args)
    {
        return data + (args + ...);
    }

    auto add_them(auto... args) // C++20 required
    {
        return data + (args + ...);
    }

    int data = 7;
};

int main()
{
    auto f = Foo{};

    auto greet = std::mem_fn(&Foo::display_greeting);
    greet(f);

    auto print_num = std::mem_fn(&Foo::display_number);
    print_num(f, 42);

    auto access_data = std::mem_fn(&Foo::data);
    std::cout << "data: " << access_data(f) << '\n';

    auto add_xy = std::mem_fn(&Foo::add_xy);
    std::cout << "add_xy: " << add_xy(f, 1, 2) << '\n';

    auto u = std::make_unique<Foo>();
    std::cout << "access_data(u): " << access_data(u) << '\n';
    std::cout << "add_xy(u, 1, 2): " << add_xy(u, 1, 2) << '\n';

    auto add_many = std::mem_fn(&Foo::add_many<short, int, long>);
    std::cout << "add_many(u, ...): " << add_many(u, 1, 2, 3) << '\n';

    auto add_them = std::mem_fn(&Foo::add_them<short, int, float, double>);
    std::cout << "add_them(u, ...): " << add_them(u, 5, 7, 10.0f, 13.0) << '\n';
}

⭐ Output ⭐

and it prints exactly this, no cap:

Hello, world.
number: 42
data: 7
add_xy: 10
access_data(u): 7
add_xy(u, 1, 2): 10
add_many(u, ...): 13
add_them(u, ...): 42

🌈 Defect reports 🌈

these behavior-changing defect reports were applied retroactively to previously published C++ standards β€” so like, the past got edited, main character energy honestly πŸ‘‘

  • LWG 2048 β€” applied to C++11 β€” behavior as published: unnecessary overloads provided β€” correct behavior: removed
  • LWG 2489 β€” applied to C++11 β€” behavior as published: noexcept not required β€” correct behavior: required

🧚 See also 🧚

your little crew of related functions, go say hi:

  • function (C++11) β€” wraps callable object of any copy constructible type with specified function call signature (class template)
  • move_only_function (C++23) β€” wraps callable object of any type with specified function call signature (class template)
  • bind (C++11) β€” binds one or more arguments to a function object (function template)

and that's the whole page bestie, you ate that πŸ«ΆπŸ’– sourced from http://cppreference.com, version 2024.06.10, filed under std::mem_fn(3) β€” go forth and wrap some pointers to members, mwah πŸ’‹πŸŽ€

πŸ“¦ where this page comes from