簡體   English   中英

我可以創建一個接受函數和函子作為參數的謂詞嗎?

[英]Can I create a predicate that will accept both functions and functors as a parameter?

我正在研究C ++中的一個問題,它涉及大量數據的大量子集和轉換操作。 為此,我創建了一個map函數和list comprehensions之類的東西。 我發現我寫的一堆謂詞也有反轉,所以我需要寫:

template <typename type_t>
bool HasTenFoo(const type_t &t) {
  return t.foo >= 10.0;
}

template <typename type_t>
bool DoesntHaveTenFoo(const type_t &t) {
  return t.foo < 10.0;
}

這些都不是一個真實的例子,但它們具有代表性。 我也使用了相當數量的仿函數:

class HasEnoughFoo {
public:
  HasEnoughFoo (double bar) { this->bar = bar; }
  template<typename type_t>
  bool operator()(const type_t &t) const { return t.foo >= bar; }
private:
  double bar;
};

其中一些也應該反轉。 我不想不必要地重復代碼,而是編寫一個將謂詞作為參數並將返回該謂詞的(反轉值)的仿函數。 我的拳頭切成一個在下面:

/* -- Returns the opposite of some other predicate -------------------------- */

template<typename predicate_t>
class Not {
public:
  template <typename predicate_t>
  Not(predicate_t *p) { predicate = p; }

  template <typename type_t>
  bool operator()(const type_t &t) const {
    return !(*predicate)(t);
  }

private:
  predicate_t *predicate;
};

我會用以下的東西來稱呼它:

new_list = old_list.subset(Not<HasEnoughFoo>(&HasEnoughFoo(10.0));

要么

new_list = old_list.subset(Not<HasTenFoo>(&HasTenFoo));

predicate_t是像HasEnoughFoo這樣的HasEnoughFoo函數時,這似乎很HasEnoughFoo ,但是當predicate_t引用像HasTenFoo這樣的常規函數​​時HasTenFoo

Visual Studio抱怨'HasTenFoo' is not a valid template type argument for parameter 'predicate_t' 有沒有辦法編寫一個可以與函子和函數一起使用的Not()謂詞,或者我注定要編寫幾十個謂詞及其反轉?

這是你的代碼工作的一個例子(我刪除了foo成員,所以它只適用於雙打)。

template <typename type_t>
bool HasTenFoo(const type_t &t) {
  return t >= 10.0;
}

class HasEnoughFoo {
public:
  HasEnoughFoo (double bar) { this->bar = bar; }
  template<typename type_t>
  bool operator()(const type_t &t) const { return t >= bar; }
private:
  double bar;
};


template<typename predicate_t>
class Not {
public:
  Not(predicate_t p): predicate(p) { }

  template <typename type_t>
  bool operator()(const type_t &t) const {
    return !predicate(t);
  }

private:
  predicate_t predicate;
};

template <class predicate_type>
Not<predicate_type> Negate(predicate_type p)
{
    return p;
}

#include <iostream>
int main()
{
    std::cout << Negate(HasTenFoo<double>)(11.0) << '\n';
    std::cout << Negate(HasEnoughFoo(13.0))(11.0) << '\n';
}

一些重要的說明:

Not的構造函數使用初始化列表。 這消除了謂詞類型具有默認構造函數(HasEnoughFoo沒有)的要求。

你肯定不想搞亂指向謂詞的指針。 功能對象應該是輕量級的對象,可以無需擔心地復制。

因為Not是一個模板類,具有可能復雜的模板參數,但您通常只將其用作臨時(作為獲取謂詞的函數的未命名參數),添加一個模板函數,為您推導出復雜類型(遍布標准庫的技巧) - 這里是Negate

你有兩個大問題。

第一個問題是HasTenFoo是一個模板函數。 模板實際上並不存在; 你不能取一個地址,因為它不存在。 但是,確實存在模板的實例化。 &HasTenFoo是非法的, &HasTenFoo<Bar>是合法的。 HasTenFoo<Bar>是指HasTenFoo模板的特定實例。

第二個問題是Not類的模板參數必須是您傳遞它的函數的類型 如果你給它HasTenFoo<Bar> ,模板參數應該是bool(*)(const Bar&)

所以,正確的版本將是

Not<bool(*)(const Bar&)>(&HasTenFoo<Bar>)

請注意,要使其適用於函數和仿函數,您必須存儲對象/函數的副本,而不是指向它們的指針。 這是首選方法; 所有采用仿函數的標准庫函數都保留內部副本。

有內置的not1謂詞。 它需要一個謂詞並否定它。 這適用於任何派生自unary_predicate的謂詞。 這將消除您的手動謂詞的需要。

它和ptr_fun的某種組合可能會達到你想要的效果。

編輯,像這樣的東西可能會起作用(警告,完全未經測試,甚至不確定這將編譯)

int factorial (int x) {
    ....
}
std::transform (d.begin (), d.end (), v.begin (), std::ptr_fun (factorial));
std::transform (d.begin (), d.end (), v.begin (), not1(std::ptr_fun (factorial)));

你可以使用boost :: lambda 它允許您使用少量代碼構造謂詞和其他函子。

你做錯了。 函數對象慣用法接受函數指針或函數對象。 另外,您不正確地獲取了模板函數的地址。 您沒有指定類型。 此外,據我所知,實例化像operator()這樣的模板運算符需要顯式的.operator()語法,大多數人都不會使用。 此外,您生成的函數對象錯誤。 讓我演示給你看。

template<typename type_t> class HasEnoughFoo {
public:
  HasEnoughFoo (double bar) { this->bar = bar; }
  bool operator()(const type_t &t) const { return t.foo >= bar; }
private:
  double bar;
};

template<typename predicate_t, typename type_t>
class Not {
public:
  Not(const predicate_t& p) { predicate = p; }

  bool operator()(const type_t &t) const {
    return !(predicate(t));
  }

private:
  predicate_t predicate;
};

現在,這個functionoid可以接受接受正確參數的函數指針或函數對象。

typedef type_t MyCustomType;
new_list = old_list.subset(Not<HasEnoughFoo<type_t>>(&HasEnoughFoo<type_t>(10.0));
new_list = old_list.subset(Not<decltype(HasTenFoo<&type_t)>>(&HasTenFoo<type_t>));

實質上:
將模板運算符放在函數類的類型上,而不是放在運算符或構造函數上。
函數必須有他們的完整模板參數來獲取他們的地址 - 編譯器如何知道你想要的函數地址?

請原諒我使用decltype,我不能打擾輸入函數簽名。 在此插入實際簽名。

函數仿函數,這意味着如果你正確地完成所有事情,你的Not模板應該按原樣運行。

首先,你的Not模板聲明中有一個明顯的錯誤 - 圍繞構造函數的一些奇怪的額外template 它應該是這樣的

template<typename predicate_t>
class Not {
public:
  Not(predicate_t *p) { predicate = p; }

  template <typename type_t>
  bool operator()(const type_t &t) const {
    return !(*predicate)(t);
  }

private:
  predicate_t *predicate;
};

那個額外的template在那里做什么我不知道。 如果您正在嘗試創建模板轉換構造函數,那么您做錯了。 也許你發布的不是真正的代碼。

現在,要將它與HasTenFoo一起使用,我們只需按照以下步驟操作即可

new_list = old_list.subset(Not<bool(const LIST_ELEMENT&)>(&HasTenFoo));

或者,使其更具可讀性

typedef bool OrdinaryFuncPredicate(const LIST_ELEMENT&);
new_list = old_list.subset(Not<OrdinaryFuncPredicate>(&HasTenFoo));

請注意用作Not模板的模板參數的類型。 您在示例中使用的內容沒有意義(它是一個值,而不是一個類型,這正是編譯器明確告訴您的)。 由於您使用函數作為謂詞,因此必須將函數類型指定為模板參數。 此函數類型應將list元素類型作為函數參數。 從您的原始帖子中不清楚列表元素類型是什么,所以我只使用LIST_ELEMENT名稱作為替代。

暫無
暫無

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

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