2010-08-05 72 views
3

我正在研究C++中的一個問題,該問題涉及大量數據的大量子集和轉換操作。爲此,我創建了一個map函數和類似列表推導。我發現了一堆,我也寫了謂詞有逆,所以我需要寫:我可以創建一個將接受函數和函數作爲參數的謂詞嗎?

​​

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 ,但當predicate_t引用像HasTenFoo這樣的常規函數​​時失敗。

Visual Studio抱怨說'HasTenFoo' is not a valid template type argument for parameter 'predicate_t'。有什麼辦法可以編寫一個Not()謂詞來處理函數和函數,或者我註定要寫幾十個謂詞及其逆函數嗎?

回答

5

下面是您的代碼工作示例(我刪除了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'; 
} 

一些重要注意事項:

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

你絕對不想混淆謂詞的指針。函數對象應該是輕量級的對象,可以無需擔心地複製。

因爲Not是一個模板類,它有一個潛在的複雜模板參數,但通常只是將它用作臨時的(作爲一個帶有謂詞的函數的未命名參數),添加一個模板函數來推導複雜爲你輸入(在整個標準庫中使用的技巧) - 在這裏Negate

+0

完美 - 這正是我所尋找的。我沒有意識到,使用初始化列表將刪除默認構造函數的要求(這是我通過指針而不是按值傳遞的)。用於隱藏模板複雜性的否定功能也很漂亮。謝謝! – 2010-08-05 16:08:09

3

你有兩個大問題。

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

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

所以,正確的版本將是

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

注意,對於這兩個函數和仿函數的工作,你就必須存儲對象/函數,而不是一個指向它們的副本。這是首選方法;所有采用函子的標準庫函數都會保留一個內部副本。

+0

謂詞是一個指向predicate_t的指針。在我看來,這是不必要的,如果謂詞是按值存儲的,它會使事情變得更容易。但事實並非如此。模板參數不應該是一個函數而不是一個函數指針呢? – 2010-08-05 15:52:52

+0

函數是指針。 &HasTenFoo 相當於HasTenFoo 。我只是在模仿他的例子。 – 2010-08-05 15:56:10

+0

是的,但在不是私有成員謂詞的類型爲bool(****)(const Bar&),不是嗎? – 2010-08-05 16:02:11

3

還有內置的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))); 
+0

我對文檔的閱讀表明not1只適用於仿函數。它是否也適用於免費功能? – 2010-08-05 15:31:29

+0

@Bill Carey,ptr_fun可用於將一個免費函數轉換爲一個函子。我從來沒有使用它,所以不能提供任何示例代碼。 – Glen 2010-08-05 15:35:07

+0

謝謝 - 我會試試看。還沒有挖掘標題盡我所能! – 2010-08-05 15:37:50

0

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

0

你做錯了。函數對象成語接受函數指針或函數對象。另外,您不正確地使用了模板函數的地址。你沒有指定類型。此外,據我所知,實例化模板運算符(如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; 
}; 

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

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,我無法打擾輸入函數簽名。在這裏插入實際簽名。

0

功能仿函數,這意味着如果您正確地做一切事情,您的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這個名字作爲替代。

相關問題