2016-08-02 2 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; 
}; 

Первоначально, я использовал функции лямбда, но это создало много повторяющегося кода мусора. Поэтому вместо этого я хочу использовать предикатные классы, которые используют наследование. Например:

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); 
} 

Теперь мы, безусловно, стили собираемся использовать лямбды в тех случаях, когда код не будет повторяться (например, какой-нибудь особый случай). Для этого 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

@ Холл Мне нужно, чтобы наследование работало правильно. Когда они передаются по значению, объекты, похоже, теряют перегруженные функции, которые называются [срезы объектов] (http://stackoverflow.com/a/274634/607407). –

+1

Да, но 'const &' не * pass by value *, это * pass by reference *, и он хорошо работает с виртуальной функцией. Но на самом деле, у вас будет такая же проблема, см. Мой ответ для реального решения на 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

Посмотрите на [std :: function's constructors] (http://en.cppreference.com/w/cpp/utility/functional/function/function): Функтор должен быть вызван с заданным аргументом. Для виртуальной части вам необходимо передать функтор через 'std :: ref', чтобы избежать нарезки. – Jarod42

+0

@ Jarod42 Если вы построите предикат во время вызова (как и вы), нет среза, так как выведенный тип для 'F' будет типом дочернего элемента напрямую. – Holt

1

Вы хотите полиморфизм, и вы не хотите использовать заголовок lambdas в стиле шаблона. И вы хотите иметь несколько случаев по умолчанию.

Правильный ответ - выкинуть свой класс Predicate.

Использование using Predicate = std::function<bool(const QString&)>;.

Далее обратите внимание, что ваши подтипы Predicate в основном являются фабриками (конструктор - завод) для Predicate с дополнительным состоянием.

Для 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::function и std::shared_ptr<yourtype> без вашей программы плохо не образуется, никаких диагностических требуется.

Даже если бы вы могли, std::function не лямбда, а лямбда-это не std::function. Таким образом, ваш оператор преобразования не будет работать.

Смежные вопросы