2016-08-02 81 views
1

我有一個類作爲謂詞從列表中選擇值。我可以定義從std :: function到std :: shared_ptr的隱式轉換<MyClass>?

class Predicate { 
public: 
    // In this example, I am using QString as value type 
    // this is not what happens in actual code, where more complex data is being validated 
    virtual bool evaluate(const QString& val) const = 0; 
}; 

最初我使用了lambda函數,但是這造成了很多重複的垃圾代碼。相反,我想使用使用繼承的謂詞類。例如:

class PredicateMaxLength: public RowPredicate { 
public: 
    PredicateMaxLength(const int max) : maxLength(max) {} 
    virtual bool evaluate(const QString& val) const {return val.length()<maxLength;} 
protected: 
    const int maxLength; 
}; 

要允許繼承做的契稅,指針被給予,而不是值:

class SomeDataObject { 
    // Removes all values that satisfy the given predicate 
    int removeValues(const std::shared_ptr<Predicate> pred); 
} 

現在我們肯定還是老樣子將使用lambda表達式的情況下,代碼不會重複(如一些特殊情況)。爲此,PredicateLambda已創建:

typedef std::function<bool(const QString& val)> StdPredicateLambda; 
class PredicateLambda: public Predicate { 
public: 
    PredicateLambda(const StdPredicateLambda& lambda) : RowPredicate(), callback_(lambda) {} 
    virtual bool evaluate(const QString& val) const override {return callback_(val);} 
protected: 
    const StdPredicateLambda callback_; 
}; 

這個討厭的效果是,每當使用拉姆達,它必須被包裹成PredicateLambda構造:

myObject.deleteItems(std::make_shared<PredicateLambda>([]->bool{ ... lambda code ... })); 

這是醜陋的。我有兩種選擇:

  • 對於接受謂詞的每個函數,都有一個超載來執行上面看到的轉換。這種複製的頭文件的方法數
  • 有從std::function<bool(const QString& val)>std::shared_ptr<Predicate>的隱式轉換這將執行此:

    std::shared_ptr<Predicate> magicImplicitConversion(const StdPredicateLambda& lambdaFn) { 
        return std::make_shared<PredicateLambda>(lambdaFn); 
    } 
    

我來這裏問了第二個選項是否是可能的。如果是,它是否有風險?

+0

爲什麼使用'shared_ptr'而不是'const'?在所有情況下,如果你可以使'removeValues'成爲模板化的方法。 – Holt

+0

@Holt我需要繼承才能正常工作。當通過值傳遞時,對象似乎失去了重載函數,稱爲[對象切片](http://stackoverflow.com/a/274634/607407)。 –

+1

是的,但'const&'不是*傳值*,它是*通過引用*,並且它與虛函數很好地協作。但實際上,您會遇到同樣的問題,請參閱我的答案以獲取真正的C++解決方案。 – Holt

回答

2

如果你不想使用template不是公開的代碼,你可以使用std::function

class SomeDataObject { 
    // Removes all values that satisfy the given predicate 
    int removeValues(std::function<bool(const QString&)> pred); 
}; 

和你的謂語

class PredicateMaxLength { 
public: 
    explicit PredicateMaxLength(int max) : maxLength(max) {} 
    bool operator()(const QString& val) const {return val.length()<maxLength;} 
protected: 
    int maxLength; 
}; 

所以,你可以使用

SomeDataObject someDataObject; 

someDataObject.removeValues(PredicateMaxLength(42)); 
someDataObject.removeValues([](const QString& s) { return s.size() < 42; }); 
+0

是否有用'()'運算符隱式轉換爲'std :: funtion'的對象?虛擬方法仍然有效嗎?目前,我在最頂層的Predicate類中實現了'()'。 –

+0

看看[函數的構造函數](http://en.cppreference.com/w/cpp/utility/functional/function/function):函數應該可以用給定的參數調用。對於虛擬部分,您必須通過'std :: ref'傳遞函數來避免切片。 – Jarod42

+0

@ Jarod42如果在調用過程中構造謂詞(與您一樣),則不會進行切片,因爲'F'的推導類型將直接作爲子類型。 – Holt

1

你想要多態性,並且你不想使用模板風格的頭lambda。而且你希望能夠有一些默認情況。

正確的答案是扔掉你的Predicate類。

使用using Predicate = std::function<bool(const QString&)>;

接下來,請注意您的Predicate子類型基本上是工廠(構造函數是工廠)Predicate s有一些額外的狀態。

對於std::function,這樣的工廠只是返回Predicate的函數。

using Predicate = std::function<bool(const QString&)>; 

Predicate PredicateMaxLength(int max) { 
    return [max](QString const& str){ return val.length()<max; } 
} 

其中PredicateMaxLength的主體進入cpp文件。

如果你的Predicate派生類有一個非常複雜的狀態集,只需給它一個operator()並將其存儲在std::function內。 (在極少數情況下,你有一些狀態你應該存儲在一個共享的ptr中,只需將它存儲在一個共享的ptr中)。

A std::function<Signature>是一種多態的常規類型。它使用了一種稱爲類型擦除的技術,既是一種價值又是多態,但實際上你可以稱之爲魔術。

當您傳遞一個對象,該對象的唯一作業將被一些參數集調用並返回一些值時,它是正確的類型。


直接回答你的問題,不,你不能定義一個std::functionstd::shared_ptr<yourtype>之間的轉換操作符不使你的程序生病形成,沒有診斷需要。

即使可以,std::function不是lambda,並且lambda不是std::function。所以你的轉換操作符不起作用。

相關問題