0

我有一些模板代码,它采用指向类的共享指针并调用函数或方法。问题来了,如果被调用的方法被定义为const.

例子:

struct Y {}; 
struct X
{
        const Y Go() const { return Y{}; }
        const Y Go2() { return Y{}; }
};

Y f1( std::shared_ptr<X>  ) { return Y{}; }

template< typename FUNC, typename ... ARGS >
auto Do( std::shared_ptr<X>& ptr, FUNC&& f, ARGS&& ... args )
{
    return f( ptr, std::forward<ARGS>(args)... );
}

template < typename CLASS, typename RET, typename ... ARGS>
auto Do( std::shared_ptr<X>& base_ptr, RET (CLASS::*mem_ptr)( ARGS...), ARGS&& ... args )->RET
{
    return (base_ptr.get()->*mem_ptr)( std::forward<ARGS>(args)...);
}

// Any chance to avoid the full duplication of the code here
// to define the member pointer to a const method?

template < typename CLASS, typename RET, typename ... ARGS>
auto Do( std::shared_ptr<X>& base_ptr, RET (CLASS::*mem_ptr)( ARGS...) const, ARGS&& ... args )->RET
{
    return (base_ptr.get()->*mem_ptr)( std::forward<ARGS>(args)...);
}

int main()
{
    auto xptr = std::make_shared<X>();
    Y y1 = Do( xptr, &X::Go );
    Y y2 = Do( xptr, &X::Go2 );
    Y y3 = Do( xptr, &f1 );
}

我的问题是RET (CLASS::*mem_ptr)( ARGS...) const. 我只是想停止为 const 复制整个代码。在现实世界的代码中,该函数再次调用另一个模板化的函数,这导致此处重复了大量代码。

有没有机会摆脱 const 成员指针的特化?

4

3 回答 3

4

在 C++17 中,我将使用带有 a 的单个模板化函数,if constexpr并检查是否可以将f其作为模板化成员函数调用std::is_invocable,然后使用std::invoke它来调用它:

template< typename FUNC, typename ... ARGS >
auto Do( std::shared_ptr<X>& ptr, FUNC&& f, ARGS&& ... args ) {
    if constexpr (std::is_invocable_v<FUNC, decltype(ptr), ARGS...>) {
        return std::invoke(f, ptr, std::forward<ARGS>(args)... );
    }
    else {
        return std::invoke(f, ptr.get(), std::forward<ARGS>(args)... );
    }
}

在 C++17 之前,您可以有两种重载:一种用于非成员函数,另一种用于成员函数。然后,您可以使用 SFINAE 根据可调用对象的类型禁用其中一个(使用类似于 的东西std::is_invocable)。

于 2019-01-14T16:53:08.030 回答
3

你可能会这样做:

template< typename FUNC, typename ... ARGS >
auto Do( std::shared_ptr<X>& ptr, FUNC&& f, ARGS&& ... args )
-> decltype((f(ptr, std::forward<ARGS>(args)... )))
{
    return f( ptr, std::forward<ARGS>(args)... );
}

template<typename MemberF, typename ... ARGS>
auto Do(std::shared_ptr<X>& base_ptr, MemberF mem_ptr, ARGS&& ... args)
-> decltype((base_ptr.get()->*mem_ptr)( std::forward<ARGS>(args)...))
{
    return (base_ptr.get()->*mem_ptr)( std::forward<ARGS>(args)...);
}
于 2019-01-14T16:53:37.357 回答
1

这是一个不需要 SFINAE 的 C++14 版本,它依赖于以下事实:

  • const Y (X::*)()U1 X::*U1 = const Y();相同
  • connt Y (X::*)() constU2 X::*与相同U2 = const Y() const
template< typename FUNC, typename ... ARGS >
auto Do( std::shared_ptr<X>& ptr, FUNC&& f, ARGS&& ... args )
{
    return f( ptr, std::forward<ARGS>(args)... );
}

template < typename CLASS, typename U, typename ... ARGS>
auto Do( std::shared_ptr<X>& base_ptr, U CLASS::*mem_ptr, ARGS&& ... args )
{
    return (base_ptr.get()->*mem_ptr)( std::forward<ARGS>(args)...);
}

发布不同的答案,因为这与第一个完全不同,而且两者都很有趣(在我看来)。

于 2019-01-14T17:42:33.493 回答