簡體   English   中英

避免非類類型的指向成員函數

[英]avoid pointer-to-member-function for non-class type

我正在編寫一種容器類,我想提供一個apply方法來評估容器內容的函數。

template<typename T>
struct Foo
{
    T val;

    /** apply a free function */
    template<typename U> Foo<U> apply(U(*fun)(const T&))
    {
        return Foo<U>(fun(val));
    }

    /** apply a member function */
    template<typename U> Foo<U> apply(U (T::*fun)() const)
    {
        return Foo<U>((val.*fun)());
    }
};

struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error

最后一行產生error: creating pointer to member function of non-class type 'const int' 即使我只是實例化了Foo而沒有使用apply 所以我的問題是:當T是非類型類型時,如何有效地刪除第二個重載?

注意:我也試過只有一個重載采用std::function<U(const T&)> 這種方法有效,因為函數指針和成員函數指針都可以轉換為std::function ,但是這種方法有效地禁用了U模板推導,這使得用戶代碼的可讀性降低。

使用std::invoke有助於實現和閱讀更容易

template<typename T>
struct Foo
{
    T val;

    template<typename U> auto apply(U&& fun)
    {
        return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
    }
};

struct Bar{};
template class Foo<Bar>;
template class Foo<int>;

但是,如果函數重載,則無法編譯

int f();
double f(const Bar&);
Foo<Bar>{}.apply(f);  // Doesn't compile

解決這個問題的方法是使用仿函數

Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });

這也使其與成員函數調用更加一致

Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });

為了消除第二次過載,您需要將其設為模板並讓SFINAE工作,例如:

template<typename T>
struct Foo
{
    T val;

    //...

    /** apply a member function */
    template<typename U, typename ObjT>
    Foo<U> apply(U (ObjT::*fun)() const)
    {
        return Foo<U>((val.*fun)());
    }
};

或者,您可以完全刪除第二個重載,並使用lambda或std::bind:

#include <functional> // for std::bind

template<typename T>
struct Foo
{
    T val;

    /** apply a member function */
    template<typename U, typename FuncT>
    Foo<U> apply(FuncT&& f)
    {
        return {f(val)};
    }
};

struct SomeType
{
    int getFive() { return 5; }
};

int main()
{
    Foo<SomeType> obj;

    obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
    obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}

當T是非類型類型時,如何有效地刪除第二個重載?

如果你至少可以使用C ++ 11(如果你試過std::function我想你可以使用它),你可以使用SFINAE和std::enable_if

   template <typename U, typename V>
   typename std::enable_if<std::is_class<V>{}
                        && std::is_same<V, T>{}, Foo<U>>::type
      apply(U (V::*fun)() const)
    { return Foo<U>((val.*fun)()); }

強加T是一個階級。

注意,你不能直接檢查T ,這是類的模板參數,但你必須通過V類型,即特定方法的模板類型。

但是你也可以強加TV是相同的類型( && std::is_same<V, T>{} )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM