Member Function Pointers and Binding

Function Pointers with Default Arguments

Can we use member function pointers with member functions that have default arguments?

Abstract art representing computer programming

Yes, you can use member function pointers with member functions that have default arguments in C++. However, there are some important considerations and limitations to be aware of.

Basic Usage

When creating a pointer to a member function with default arguments, the pointer doesn't carry information about the default arguments. You must provide all arguments when calling through the pointer:

#include <iostream>

class MyClass {
 public:
  void foo(int a, int b = 10) {
    std::cout << "a: " << a << ", b: " << b;
  }
};

int main() {
  void (MyClass::*ptr)(int, int) = &MyClass::foo;

  MyClass obj;

  // (obj.*ptr)(5); // Error: too few arguments 
  (obj.*ptr)(5, 20);  // OK 
}
a: 5, b: 20

Overloading and Default Arguments

If you have overloaded functions where one version uses default arguments, you need to be explicit about which version you're pointing to:

#include <iostream>

class MyClass {
public:
  void bar(int a) {
    std::cout << "bar(int): " << a << '\n';
  }

  void bar(int a, int b = 10) {
    std::cout << "bar(int, int): "
      << a << ", " << b << '\n';
  }
};

int main() {
  void (MyClass::*ptr1)(int) = &MyClass::bar;
  void (MyClass::*ptr2)(int, int) = static_cast<
    void (MyClass::*)(int, int)>(&MyClass::bar);

  MyClass obj;
  (obj.*ptr1)(5); // Calls bar(int)
  (obj.*ptr2)(5, 20); // Calls bar(int, int)
}
bar(int): 5
bar(int, int): 5, 20

Using std::function

When using std::function, you need to specify the full function signature, including all parameters:

#include <functional>
#include <iostream>

class MyClass {
 public:
  void baz(int a, std::string b = "default") {
    std::cout << "a: " << a << ", b: " << b;
  }
};

int main() {
  std::function<void(
    MyClass&, int, std::string
  )> func = &MyClass::baz;

  MyClass obj;
  // func(obj, 5); // Error: too few arguments 
  func(obj, 5, "custom");  // OK 
}
a: 5, b: custom

Wrapper Functions

To leverage default arguments, you can create wrapper functions:

#include <iostream>

class MyClass {
 public:
  void process(int a, int b = 10, int c = 20) {
    std::cout << "a: " << a << ", b: "
      << b << ", c: " << c << '\n';
  }
};

void wrapper(MyClass& obj, int a) {
  obj.process(a);
}

int main() {
  void (*ptr)(MyClass&, int) = wrapper;

  MyClass obj;
  ptr(obj, 5);
}
a: 5, b: 10, c: 20

We can create a similar solution using a lambda:

#include <functional>
#include <iostream>

class MyClass {
public:
  void process(int a, int b = 10, int c = 20) {
    std::cout << "a: " << a << ", b: " << b <<
      ", c: " << c << '\n';
  }
};

int main() {
  auto bound = [](MyClass& obj, int a,
                  int b = 10, int c = 20){
    obj.process(a, b, c);
  };

  MyClass obj;
  // Uses default for b and c
  bound(obj, 5);

  // Uses default for c
  bound(obj, 5, 15);

  // Specifies all arguments
  bound(obj, 5, 15, 25);
}
a: 5, b: 10, c: 20
a: 5, b: 15, c: 20
a: 5, b: 15, c: 25

Template Solutions

For more flexibility, you can use templates to create generic wrappers:

#include <iostream>

class MyClass {
public:
  void process(int a, int b = 10, int c = 20) {
    std::cout << "a: " << a
      << ", b: " << b
      << ", c: " << c << '\n';
  }
};

template <typename T, typename... Args>
auto makeWrapper(void (T::*func)(Args...)) {
  return [func](T& obj, auto... args){
    // Capture default values
    int b = 10, c = 20;

    // Determine how many arguments are passed
    if constexpr (sizeof...(args) == 1) {
      // Only a is provided
      (obj.*func)(args..., b, c);
    } else if constexpr (sizeof...(args) == 2) {
      // a and b are provided
      (obj.*func)(args..., c);
    } else {
      // All are provided
      (obj.*func)(args...);
    }
  };
}

int main() {
  auto wrapper = makeWrapper(&MyClass::process);

  MyClass obj;

  // Uses default for b and c
  wrapper(obj, 5); 

  // Uses default for c
  wrapper(obj, 5, 15); 

  // Specifies all arguments
  wrapper(obj, 5, 15, 25);
}
a: 5, b: 10, c: 20
a: 5, b: 15, c: 20
a: 5, b: 15, c: 25

Limitations and Considerations

  • Member function pointers don't store information about default arguments. This information is part of the function declaration, not the pointer.
  • When using function pointers or std::function, you generally need to provide all arguments explicitly.
  • Default arguments are resolved at compile-time, while function pointers are typically used for runtime polymorphism.

Best Practices

  • Be explicit about function signatures when working with member function pointers and default arguments to avoid ambiguity.
  • Consider using wrapper functions or std::bind to leverage default arguments when needed.
  • Document the expected usage clearly, especially when the function pointed to has default arguments.

While member function pointers can be used with functions that have default arguments, it's important to understand the limitations and use appropriate techniques to handle default values when needed.

This often involves creating wrappers or using standard library utilities to achieve the desired behavior.

Answers to questions are automatically generated and may not have been reviewed.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 55 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved