我可以定义从 std::function 到 std::shared_ptr<MyClass> 的隐式转换吗?
Can I define implicit conversion from std::function to std::shared_ptr<MyClass>?
我有一个 class 作为列表中 select 值的谓词。
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 函数,但这会产生很多重复的垃圾代码。因此,我想使用使用继承的谓词 classes。例如:
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_;
};
这样做的恶劣影响是,无论何时使用 lambda,都必须将其包装到 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);
}
我是来问第二个方案可不可以的。如果是,它有任何风险吗?
如果您不想使用 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; });
你想要多态性,你不想使用 template-style header lambdas。并且您希望能够有一些默认情况。
正确答案是扔掉你的 Predicate
class.
使用using Predicate = std::function<bool(const QString&)>;
.
接下来,请注意您的 Predicate
sub-types 基本上是 Predicate
具有一些额外状态的工厂(构造函数是工厂)。
对于std::function
,这样的工厂只是一个函数returning Predicate
。
using Predicate = std::function<bool(const QString&)>;
Predicate PredicateMaxLength(int max) {
return [max](QString const& str){ return val.length()<max; }
}
PredicateMaxLength
的 body 在 cpp 文件中。
如果您的 Predicate
派生的 class 有一组非常复杂的状态,只需给它一个 operator()
并将其存储在 std::function
中。 (在极少数情况下,您应该将某些状态存储在共享 ptr 中,只需将其存储在共享 ptr 中即可)。
A std::function<Signature>
是多态的常规类型。它使用一种称为类型擦除的技术,既是值又是多态的,但实际上你可以称之为魔法。
当你传递一个 object 时,它是正确的类型,它的唯一工作是用一些参数集和 return 一些值来调用。
要直接回答你的问题,不,你不能在 std::function
和 std::shared_ptr<yourtype>
之间定义转换运算符而不会使你的程序格式错误,不需要诊断。
即使可以,std::function
也不是 lambda,lambda 也不是 std::function
。所以你的转换运算符不起作用。
我有一个 class 作为列表中 select 值的谓词。
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 函数,但这会产生很多重复的垃圾代码。因此,我想使用使用继承的谓词 classes。例如:
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_;
};
这样做的恶劣影响是,无论何时使用 lambda,都必须将其包装到 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); }
我是来问第二个方案可不可以的。如果是,它有任何风险吗?
如果您不想使用 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; });
你想要多态性,你不想使用 template-style header lambdas。并且您希望能够有一些默认情况。
正确答案是扔掉你的 Predicate
class.
使用using Predicate = std::function<bool(const QString&)>;
.
接下来,请注意您的 Predicate
sub-types 基本上是 Predicate
具有一些额外状态的工厂(构造函数是工厂)。
对于std::function
,这样的工厂只是一个函数returning Predicate
。
using Predicate = std::function<bool(const QString&)>;
Predicate PredicateMaxLength(int max) {
return [max](QString const& str){ return val.length()<max; }
}
PredicateMaxLength
的 body 在 cpp 文件中。
如果您的 Predicate
派生的 class 有一组非常复杂的状态,只需给它一个 operator()
并将其存储在 std::function
中。 (在极少数情况下,您应该将某些状态存储在共享 ptr 中,只需将其存储在共享 ptr 中即可)。
A std::function<Signature>
是多态的常规类型。它使用一种称为类型擦除的技术,既是值又是多态的,但实际上你可以称之为魔法。
当你传递一个 object 时,它是正确的类型,它的唯一工作是用一些参数集和 return 一些值来调用。
要直接回答你的问题,不,你不能在 std::function
和 std::shared_ptr<yourtype>
之间定义转换运算符而不会使你的程序格式错误,不需要诊断。
即使可以,std::function
也不是 lambda,lambda 也不是 std::function
。所以你的转换运算符不起作用。