为什么我应该更喜欢单独的函数而不是静态方法来进行 C++ 中的函数式编程?
Why should I prefer a separate function over a static method for functional programming in c++?
在我的另一个问题 中,人们解决了我的实际问题,然后建议我采用不同的方法来遵循最佳实践。我想更好地理解原因。
这个例子使用了一个比较器,但我真的很想知道是否有函数式编程的一般规则(例如,也许还有与 class 等相关的聚合函数)
比较器功能的示例用例:
#include <set>
int main() {
std::set<MyClass, decltype(SOMETHING_HERE)> s(SOMETHING_HERE);
return 0;
}
以下是我知道的解决方案,从最推荐到最不推荐(我相信)- 与我目前选择的顺序相反。
1.
使用函子的推荐方式(我相信):
struct MyComparator {
bool operator() (MyClass a, MyClass b) const {
return a > b; // Reversed
}
};
std::set<MyClass, MyComparator)> s;
我不喜欢:冗长;在词汇上与 MyClass 无关;我不明白为什么 s 没有构造函数参数。我对缺少参数感到困惑,因为 set
似乎混合了 up/combine 在编译时/使用类型系统(如本示例)和运行时/使用指针来决定比较算法。如果它只是一种方式或另一种方式,我会接受“就是这样”。
2.
一个对我来说更好的方法:
auto const my_comparator = [](int a, int b) {
return a > b;
};
std::set<int, decltype(my_comparator)> s(my_comparator);
它更简洁。意图很明确:它是一个函数,而不是可以添加的 class 。传递对象,而不仅仅是类型,对我来说很有意义(不能有 2 种不同的实现)。
3.
对我来说最有意义的方法(基于其他语言):
class MyClass {
public:
// More code here
static auto compare_reverse(MyClass a, MyClass b) {
return a > b;
}
};
std::set<int, decltype(&MyClass::compare_reverse)> s(&MyClass::compare_reverse);
它显然与 MyClass 相关(并且在词汇上与之相关)。看起来高效(虽然有人告诉我它不是)和简洁。
任何解释为什么推荐的顺序更好,性能,bugs/maintainability,哲学,非常感谢。
首先,请注意,如果您只想要一个 >
比较器,则有 std::greater<>
and std::greater<MyClass>
(the former is usually superior).
正在查看您列出的选项:
(1)
struct MyComparator
{
bool operator()(MyClass a, MyClass b) const
{
return a > b;
}
};
std::set<MyClass, MyComparator)> s;
正如你所说,这是推荐的方式。
not lexically tied to MyClass
按照评论里的建议,可以放在MyClass
.
里面
don't understand why s has no constructor argument
如果您不将比较器传递给构造函数,则比较器是默认构造的(如果它是默认构造的,否则会出现编译错误)。
您可以手动传递 MyComparator{}
,但没有意义。
(2)
auto my_comparator = [](int a, int b)
{
return a > b;
};
std::set<int, decltype(my_comparator)> s(my_comparator);
它完全等同于 (1),只是您必须将 my_comparator
传递给构造函数。 C++20 通过使 lambdas 可以默认构造(如果它们没有捕获)来解决这个问题,使参数变得不必要。
(3)
class MyClass
{
public:
static auto compare_reverse(MyClass a, MyClass b)
{
return a > b;
}
};
std::set<int, decltype(&MyClass::compare_reverse)> s(&MyClass::compare_reverse);
这为您提供了一个额外的功能:您可以在运行时通过将不同的函数传递给构造函数来选择比较器。
此功能带来开销:集合存储指向函数的指针(通常为 4 或 8 个字节),并且在进行比较时必须查看指针。
(3.5)
您可以将函数包装在 std::integral_constant
中。结果等同于 (1).
bool foo(int x, int y) {return x > y;}
std::set<int, std::integral_constant<decltype(&foo), foo>> s;
// Or:
using MyComparator = std::integral_constant<decltype(&foo), foo>;
std::set<int, MyComparator> s;
如果您正在处理一个现有类型,它将比较器作为一个函数提供,并且您不希望 (3) 的开销,那么这很好。
所有这些(除了 (3))都是等价的。
如果std::greater<>
适用,你应该使用它。
否则我会推荐 (1) 作为最不容易混淆的选项。
在我的另一个问题
这个例子使用了一个比较器,但我真的很想知道是否有函数式编程的一般规则(例如,也许还有与 class 等相关的聚合函数)
比较器功能的示例用例:
#include <set>
int main() {
std::set<MyClass, decltype(SOMETHING_HERE)> s(SOMETHING_HERE);
return 0;
}
以下是我知道的解决方案,从最推荐到最不推荐(我相信)- 与我目前选择的顺序相反。
1.
使用函子的推荐方式(我相信):
struct MyComparator {
bool operator() (MyClass a, MyClass b) const {
return a > b; // Reversed
}
};
std::set<MyClass, MyComparator)> s;
我不喜欢:冗长;在词汇上与 MyClass 无关;我不明白为什么 s 没有构造函数参数。我对缺少参数感到困惑,因为 set
似乎混合了 up/combine 在编译时/使用类型系统(如本示例)和运行时/使用指针来决定比较算法。如果它只是一种方式或另一种方式,我会接受“就是这样”。
2.
一个对我来说更好的方法:
auto const my_comparator = [](int a, int b) {
return a > b;
};
std::set<int, decltype(my_comparator)> s(my_comparator);
它更简洁。意图很明确:它是一个函数,而不是可以添加的 class 。传递对象,而不仅仅是类型,对我来说很有意义(不能有 2 种不同的实现)。
3.
对我来说最有意义的方法(基于其他语言):
class MyClass {
public:
// More code here
static auto compare_reverse(MyClass a, MyClass b) {
return a > b;
}
};
std::set<int, decltype(&MyClass::compare_reverse)> s(&MyClass::compare_reverse);
它显然与 MyClass 相关(并且在词汇上与之相关)。看起来高效(虽然有人告诉我它不是)和简洁。
任何解释为什么推荐的顺序更好,性能,bugs/maintainability,哲学,非常感谢。
首先,请注意,如果您只想要一个 >
比较器,则有 std::greater<>
and std::greater<MyClass>
(the former is usually superior).
正在查看您列出的选项:
(1)
struct MyComparator
{
bool operator()(MyClass a, MyClass b) const
{
return a > b;
}
};
std::set<MyClass, MyComparator)> s;
正如你所说,这是推荐的方式。
not lexically tied to MyClass
按照评论里的建议,可以放在MyClass
.
don't understand why s has no constructor argument
如果您不将比较器传递给构造函数,则比较器是默认构造的(如果它是默认构造的,否则会出现编译错误)。
您可以手动传递 MyComparator{}
,但没有意义。
(2)
auto my_comparator = [](int a, int b)
{
return a > b;
};
std::set<int, decltype(my_comparator)> s(my_comparator);
它完全等同于 (1),只是您必须将 my_comparator
传递给构造函数。 C++20 通过使 lambdas 可以默认构造(如果它们没有捕获)来解决这个问题,使参数变得不必要。
(3)
class MyClass
{
public:
static auto compare_reverse(MyClass a, MyClass b)
{
return a > b;
}
};
std::set<int, decltype(&MyClass::compare_reverse)> s(&MyClass::compare_reverse);
这为您提供了一个额外的功能:您可以在运行时通过将不同的函数传递给构造函数来选择比较器。
此功能带来开销:集合存储指向函数的指针(通常为 4 或 8 个字节),并且在进行比较时必须查看指针。
(3.5)
您可以将函数包装在 std::integral_constant
中。结果等同于 (1).
bool foo(int x, int y) {return x > y;}
std::set<int, std::integral_constant<decltype(&foo), foo>> s;
// Or:
using MyComparator = std::integral_constant<decltype(&foo), foo>;
std::set<int, MyComparator> s;
如果您正在处理一个现有类型,它将比较器作为一个函数提供,并且您不希望 (3) 的开销,那么这很好。
所有这些(除了 (3))都是等价的。
如果std::greater<>
适用,你应该使用它。
否则我会推荐 (1) 作为最不容易混淆的选项。