C++:模板元編程=>std::invoke源碼分析及其實現


在實現invoke之前,我們先看一下標准庫種invoke的使用方式

template< class F, class... Args>
std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) noexcept(/* see below */);
(C++17 起)

// 摘自cppreference
#include <functional> #include <iostream> struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_+i << '\n'; } int num_; }; void print_num(int i) { std::cout << i << '\n'; } struct PrintNum { void operator()(int i) const { std::cout << i << '\n'; } }; int main() { // 調用自由函數 std::invoke(print_num, -9); // 調用 lambda std::invoke([]() { print_num(42); }); // 調用成員函數 const Foo foo(314159); std::invoke(&Foo::print_add, foo, 1); // 調用(訪問)數據成員 std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n'; // 調用函數對象 std::invoke(PrintNum(), 18); }

標准庫的invoke函數,可以支持普通函數,成員函數,訪問數據成員,lambda

#include <iostream>
#include <functional>
// ========================================================================
// 第三步
// 調用普通函數的版本
struct _InvokeFunction {
    template<typename _Callable, typename... _Types>
    static auto _Call(_Callable&& obj, _Types&&... argv) {
        return obj(std::forward<_Types>(argv)...);
    }
};
// 調用成員函數版本
struct _InvokeMemFunc {
    template<typename _Callable, typename _Obj, typename... _Types>
    static auto _Call(_Callable&& fn, _Obj&& obj, _Types&&... argv) ->
        decltype((obj->*fn)(std::forward<_Types>(argv)...)) {
        return (obj->*fn)(std::forward<_Types>(argv)...);
    }
    // 這里和stl當中方法不一樣,這里采用使用SFINAE技術
    // 編譯器會自動選擇兩者當中可調用的版本
    template<typename _Callable, typename _Obj, typename... _Types>
    static auto _Call(_Callable&& fn, _Obj&& obj, _Types&&... argv) ->
        decltype((obj.*fn)(std::forward<_Types>(argv)...)) {
        return (obj.*fn)(std::forward<_Types>(argv)...);
    }
};
// 調用成員變量
struct _InvokeMemObj {
    template<typename _Callable, typename _Obj>
    static auto _Call(_Callable&& fn, _Obj&& obj) ->
        decltype((obj->*fn)) {
        return (obj->*fn);
    }
    template<typename _Callable, typename _Obj>
    static auto _Call(_Callable&& fn, _Obj&& obj) ->
        decltype((obj.*fn)) {
        return (obj.*fn);
    }
};
// =========================================================================
// 第二步
// 第二層,篩選多參數普通函數,成員函數,數據成員
// 暫時依賴標准庫的萃取技術
template<typename _Callable, 
    typename _FirstTy,
    typename _Decayed = typename std::decay<_Callable>::type,
    bool _Is_MemFun = std::is_member_function_pointer<_Decayed>::value,
    bool _Is_MemObj = std::is_member_object_pointer<_Decayed>::value>
struct _Invoke1;
// 成員函數,標准庫當中傳遞
// _FirstTy的作用是用來判斷 _Callable的Class是否是_FirstTy的Class或者Parent Class
// 這里為了簡化不再判斷
template<typename _Callable, typename _FirstTy, typename _Decayed>
struct _Invoke1<_Callable, _FirstTy, _Decayed, true, false> :
    _InvokeMemFunc {
};
// 成員變量
template<typename _Callable, typename _FirstTy, typename _Decayed>
struct _Invoke1<_Callable, _FirstTy, _Decayed, false, true> :
    _InvokeMemObj {
};
// 普通函數
template<typename _Callable, typename _FirstTy, typename _Decayed>
struct _Invoke1<_Callable, _FirstTy, _Decayed, false, false> :
    _InvokeFunction {
};
// =========================================================================
// 第一步
// 本層先把無參數的直接篩選出來了
template<typename _Callable, typename... _Types>
struct _Invoke;
// 無參數,必定是一個普通函數
template<typename _Callable>
struct _Invoke<_Callable> :
    _InvokeFunction {
};
// 有一個或多個參數,可能是普通函數,成員函數,數據成員
template<typename _Callable, typename _FirstTy, typename... _Types>
struct _Invoke<_Callable, _FirstTy, _Types...> :
    _Invoke1<_Callable, _FirstTy> {
};
// 通過Invoke函數進行一層封裝,使其使用更加貼合實際
template<typename _Callable, typename... _Types>
auto Invoke(_Callable&& obj, _Types&&... argv) {
    return _Invoke<_Callable, _Types...>::_Call(
        std::forward<_Callable>(obj), 
        std::forward<_Types>(argv)...);
}
// ========================================================================
// 測試代碼
void sum(int a, int b, int c) {
    std::cout << a + b + c<< std::endl;
}
struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_ + i << '\n'; }
    int num_;
};
int main() {
    Foo foo(123);
    Invoke(sum, 1, 2, 3);
    Invoke([=]() {});
    Invoke(&Foo::print_add, foo, 1);
    Invoke(&Foo::print_add, &foo, 1);
    auto n = Invoke(&Foo::num_, &foo);
    auto n1 = Invoke(&Foo::num_, foo);
    return 0;
}

至此一個簡單的invoke就實現完成了,cppreference上面有基於c++17的更簡單的實現,感興趣的可以去看,這里就不再啰嗦了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM