c++invoke

Can c++ std::invoke choose appropriate lvalue or rvalue reference arg overload automatically?


Here is a toy code to show what I hope it to work like:

    int func_invk_lr(int& a) {
        cout << "call left" << endl;
        return a;
    }

    int func_invk_lr(int&& a) {
        cout << "call right" << endl;
        return a;
    }

    template<typename T>
    void call_invk_lr(T&& a) {
        std::invoke(func_invk_lr, a); // error: no matching function for call to ‘invoke(<unresolved overloaded function type>, int&)’
        std::invoke(func_invk_lr, std::forward<T>(a)); //  error: no matching function for call to ‘invoke(<unresolved overloaded function type>, int&)’
    }

    int main() {
        int a = 1;
        std::invoke(func_invk_lr, a);    //  error: no matching function for call to ‘invoke(<unresolved overloaded function type>, int&)’
        std::invoke(func_invk_lr, 1);    //  error: no matching function for call to ‘invoke(<unresolved overloaded function type>, int)’


        call_invk_lr(a);
        call_invk_lr(1); 
    }

Here are two things that I am confused:
1. can std::invoke indentify left or right args and call different functions accordingly?
2. if std::invoke can work as expected, do we still need std::forward to pass args in terms of template functions T&& ?


Solution

  • It can do that if you use lambda, std::forward is required of course:

    #include <functional>
    #include <iostream>
    #include <utility>
    
    void func_invk_lr(int& a) { std::cout << "call left\n"; }
    
    void func_invk_lr(int&& a) { std::cout << "call right\n"; }
    
    template <typename T>
    void call_invk_lr(T&& a) {
      std::invoke([](auto&& a) { func_invk_lr(std::forward<decltype(a)>(a)); },
                  std::forward<decltype(a)>(a));
    }
    
    int main() {
      int a = 1;
      std::invoke([](auto&& a) { func_invk_lr(std::forward<decltype(a)>(a)); }, a);
      std::invoke([](auto&& a) { func_invk_lr(std::forward<decltype(a)>(a)); }, 1);
    
      call_invk_lr(a);
      call_invk_lr(1);
    }
    
    // call left
    // call right
    // call left
    // call right
    

    Or a callable

    #include <functional>
    #include <iostream>
    
    struct func_invk_lr {
      void operator()(int& a) { std::cout << "call left\n"; }
      void operator()(int&& a) { std::cout << "call right\n"; }
    };
    
    int main() {
      int a = 1;
      std::invoke(func_invk_lr{}, a);
      std::invoke(func_invk_lr{}, 1);
    }
    
    // call left
    // call right