[英]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.