是否可以根据调用站点的上下文指定不同的 C++ `operator==` 功能?
Is it possible to specify different C++ `operator==` functionality depending on the context at the call site?
我正在尝试为我的自定义 classes 进行显式浮点相等比较(精确比较与近似比较)。我可以避免重载 ==
运算符并强制用户改为调用 exactly_equal
或 almost_equal
之类的函数,但这不适用于开箱即用的 std
算法。我想知道是否有一种很好的方法可以通过在调用站点强制进行一些显式运算符查找来获得两全其美(有点像 std::rel_ops
)。
例如,假设我有这个 CustomType
class 和一些 operator==
重载:
struct CustomType {
double value;
};
namespace exactly_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return lhs.value == rhs.value;
}
} // namespace exactly_equal
namespace almost_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return std::abs(lhs.value - rhs.value) < 1e-2; // This isn't the "right" way, just an example.
}
} // namespace almost_equal
使用这个 class 我可以做这样的事情,编译和运行良好:
auto main() -> int {
auto const a = CustomType{1.0/3.0};
auto const b = CustomType{0.3333};
{
using namespace exactly_equal;
if (a == b) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
if (a == b) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
使用 std
函数时 argument-dependent lookup 不起作用:
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
{
using namespace exactly_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
大多数用于添加运算符的 suggestions 涉及某种带有运算符重载的基 class 或类似于 using namespace std::rel_ops
的模式(这也无法通过参数相关的查找)。我不确定基础 class 是否有助于解决这个问题,理想情况下我想在 class 上使用这个解决方案我不拥有(并且无法修改)。
我可以为我的数据结构和算法使用显式函数和类型:
struct ExactlyEqualPredicate{
auto operator()(CustomType const& lhs, CustomType const& rhs) const -> bool {
return lhs.value == rhs.value;
}
};
struct AlmostEqualComparator{
CustomType value;
auto operator()(CustomType const& other) const -> bool {
return std::abs(value.value == other.value) < 1e-2;
}
};
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
if (std::find_if(items.begin(), items.end(), AlmostEqualComparator{value}) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
auto custom_map = std::unordered_map<CustomType,
std::string,
std::hash<CustomType>,
ExactlyEqualPredicate>{
{CustomType{0.3333}, "Exact!"},
};
if (auto iter = custom_map.find(value); iter != custom_map.end()) {
std::cout << iter->second << std::endl;
}
return 0;
}
但是当我有嵌套容器 (std::vector<std::vector<CustomType>>
) 或其他复杂结构时,这会变得非常冗长和崩溃。模板 classes 似乎有一些针对 argument-dependent lookup 的额外规则,但我并不清楚使用模板完成此操作的好方法。
目标是在不使用外部函数和 std
算法过于困难 and/or 冗长的情况下,在调用站点保持明确的相等比较(精确比较与近似比较)。是否有我遗漏的解决方案或我没有想到的一些黑魔法可能使这成为可能?我目前在我的代码库中使用 C++17。
谢谢!
除了 wrapper 定义自己的比较的类型之外,您已经列举了看似合理但有缺陷的方法。它们可能会存储指向底层对象的指针或引用以避免复制,尽管即使这样也不允许算法以不同方式解释 containers。为此,现代解决方案是使用范围(可能包含在 C++20 中)及其 可组合 转换和投影(很像您上一种方法)。
我正在尝试为我的自定义 classes 进行显式浮点相等比较(精确比较与近似比较)。我可以避免重载 ==
运算符并强制用户改为调用 exactly_equal
或 almost_equal
之类的函数,但这不适用于开箱即用的 std
算法。我想知道是否有一种很好的方法可以通过在调用站点强制进行一些显式运算符查找来获得两全其美(有点像 std::rel_ops
)。
例如,假设我有这个 CustomType
class 和一些 operator==
重载:
struct CustomType {
double value;
};
namespace exactly_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return lhs.value == rhs.value;
}
} // namespace exactly_equal
namespace almost_equal {
auto operator==(CustomType const& lhs, CustomType const& rhs) -> bool {
return std::abs(lhs.value - rhs.value) < 1e-2; // This isn't the "right" way, just an example.
}
} // namespace almost_equal
使用这个 class 我可以做这样的事情,编译和运行良好:
auto main() -> int {
auto const a = CustomType{1.0/3.0};
auto const b = CustomType{0.3333};
{
using namespace exactly_equal;
if (a == b) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
if (a == b) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
使用 std
函数时 argument-dependent lookup 不起作用:
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
{
using namespace exactly_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Exact!" << std::endl;
}
}
{
using namespace almost_equal;
// error: no match for 'operator==' (operand types are 'const CustomType' and 'const CustomType')
if (std::find(items.begin(), items.end(), value) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
}
return 0;
}
大多数用于添加运算符的 suggestions 涉及某种带有运算符重载的基 class 或类似于 using namespace std::rel_ops
的模式(这也无法通过参数相关的查找)。我不确定基础 class 是否有助于解决这个问题,理想情况下我想在 class 上使用这个解决方案我不拥有(并且无法修改)。
我可以为我的数据结构和算法使用显式函数和类型:
struct ExactlyEqualPredicate{
auto operator()(CustomType const& lhs, CustomType const& rhs) const -> bool {
return lhs.value == rhs.value;
}
};
struct AlmostEqualComparator{
CustomType value;
auto operator()(CustomType const& other) const -> bool {
return std::abs(value.value == other.value) < 1e-2;
}
};
auto main() -> int {
auto const items = std::vector<CustomType>{{1.0/3.0}};
auto const value = CustomType{0.3333};
if (std::find_if(items.begin(), items.end(), AlmostEqualComparator{value}) != items.end()) {
std::cout << "Inexact!" << std::endl;
}
auto custom_map = std::unordered_map<CustomType,
std::string,
std::hash<CustomType>,
ExactlyEqualPredicate>{
{CustomType{0.3333}, "Exact!"},
};
if (auto iter = custom_map.find(value); iter != custom_map.end()) {
std::cout << iter->second << std::endl;
}
return 0;
}
但是当我有嵌套容器 (std::vector<std::vector<CustomType>>
) 或其他复杂结构时,这会变得非常冗长和崩溃。模板 classes 似乎有一些针对 argument-dependent lookup 的额外规则,但我并不清楚使用模板完成此操作的好方法。
目标是在不使用外部函数和 std
算法过于困难 and/or 冗长的情况下,在调用站点保持明确的相等比较(精确比较与近似比较)。是否有我遗漏的解决方案或我没有想到的一些黑魔法可能使这成为可能?我目前在我的代码库中使用 C++17。
谢谢!
除了 wrapper 定义自己的比较的类型之外,您已经列举了看似合理但有缺陷的方法。它们可能会存储指向底层对象的指针或引用以避免复制,尽管即使这样也不允许算法以不同方式解释 containers。为此,现代解决方案是使用范围(可能包含在 C++20 中)及其 可组合 转换和投影(很像您上一种方法)。