如何在不同的命名空间中指定重载运算符
How can I specify an overloaded operator in a different namespace?
我在使用C++标准库时遇到问题。下面的例子没有编译:(注意,这是为了制作一个最小的例子,所以没有多大意义)
#include <algorithm>
#include <string>
#include <vector>
namespace otherns {
class Property {
public:
const std::string &getName() const { return m_name; }
private:
std::string m_name;
};
}
bool operator==(const otherns::Property &a, const otherns::Property &b) {
return a.getName() == b.getName();
}
/* Merge, second takes priority */
std::vector<otherns::Property>
merge_props(const std::vector<otherns::Property> &xs,
const std::vector<otherns::Property> &ys) {
std::vector<otherns::Property> ans = ys;
for (const auto &x : xs) {
if (std::find(ans.begin(), ans.end(), x) == ans.end()) {
ans.push_back(x);
}
}
return ans;
}
错误为"binary'==':找不到接受类型为'otherns::Property'的左侧操作数的运算符(或者没有可接受的转换)",该错误发生在std::find
的实现中的某个位置。这是MSVC,但我也尝试过clang和gcc,结果类似。
以下代码确实有效:
std::vector<otherns::Property>
merge_props(const std::vector<otherns::Property> &xs,
const std::vector<otherns::Property> &ys) {
std::vector<otherns::Property> ans = ys;
for (const auto &x : xs) {
if (std::find_if(ans.begin(), ans.end(), [&x](const otherns::Property &y) {
return x == y;
}) == ans.end()) {
ans.push_back(x);
}
}
return ans;
}
我想这与ADL/Koenig查找有关,但我真的不明白为什么找不到我的operator==
。如果我想使用find
函数的第一种更简单的形式,最好的解决方案是什么?
事实上,otherns
来自第三方库的标头,所以我不能将运算符放入该标头中。
规则非常复杂,我自己也没有完全掌握,但让我们看看我们是否能理解它们(我认为我们可以):
namespace nx {
struct X {};
}
namespace ns {
auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X')
}
auto operator==(nx::X, nx::X) { return true; }
auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}
找不到这一点的原因很简单:operator==
在使用之前没有声明。ADL还没有什么可做的。到目前为止还不错。我们理解这一点。让我们修复它:
namespace nx {
struct X {};
}
auto operator==(nx::X, nx::X) { return true; }
namespace ns {
auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
}
auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}
这行得通吗?是的,它会编译并调用我们的operator==
。这是正确的解决方案吗不。因为如果我们加上这个:
namespace nx {
struct X {};
}
auto operator==(nx::X, nx::X) { return true; } // (1)
namespace ns {
template <class T> auto operator==(T, int) { return false; } // (2)
auto foo(nx::X x1, nx::X x2) { return x1 == x2; }
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X')
}
auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}
然后,ns中的(2)将(1)隐藏在全局命名空间中,即使(1)更适合。这被称为名称隐藏,并且——再次——以任何方式都不涉及ADL。
更糟糕的是:
namespace nx {
struct X {};
}
auto operator==(nx::X, nx::X) { return true; } // (1)
namespace ns {
template <class T> auto operator==(T, T) { return false; } // (2)
auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (2)
}
auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}
将编译并静默地调用(2)
,而不是我们的运算符(1)
。
对于真实世界的上下文,请将namespace ns
想象为命名空间std
在CCD_ 11中声明的任何运算符。你的岗位上有这种情况。
正确的解决方案是:
namespace nx {
struct X {};
auto operator==(nx::X, nx::X) { return true; } // (1)
}
namespace ns {
template <class T> auto operator==(T, T) { return false; } // (2)
auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (1)
}
auto global_foo()
{
return ns::foo(nx::X{}, nx::X{});
}
这里发生的是,ADL开始从nx
引入(1)
,现在(1)
被认为与(2)
并列。但是CCD_ 16比CCD_ 17更专业,因此CCD_。
如果你不能控制namespace nx
,也不能在那里添加运算符,那么我可以建议你使用可调用函数,而不是依赖运算符。例如,使用std::find_if
来代替std::find
和您自己的谓词(lambda),在这里您可以精确地控制要调用的方法/运算符。当我说"确切地"时,我的意思是确切地:即::operator==(x1, x2)
(或您声明的任何命名空间)而不是x1 == x2
。
您可以阅读Herb Sutter Namespaces&接口原理
只需在名称空间otherns
中声明operator==
(查找将在名称空间范围内找到它)
namespace otherns {
bool operator==(const otherns::Property &a, const otherns::Property &b) {
return a.getName() == b.getName();
}
}
工作代码
您可以在第三方库的单独标头中执行此操作。
您在全局命名空间中定义了operator==
(可能被误认误导了)。通过依赖于参数的查找不会在那里找到它。
运算符应该在与其参数(其中一个)相同的命名空间中声明:
namespace otherns {
class Property {
public:
const std::string &getName() const { return m_name; }
private:
std::string m_name;
};
bool operator==(const otherns::Property &a, const otherns::Property &b) {
return a.getName() == b.getName();
}
}
这个小小的更改使您的示例能够干净地编译。
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 重载运算符new[]的行为取决于析构函数
- 为什么将值返回函数传递给重载=运算符对运算符函数有效,而对其他运算符无效
- 在 myVector 类中重载运算符 + 时出错
- 为什么常量词在重载运算符中不与 ostream 对象一起使用<<?
- 如何在 cpp 中重载运算符 +=?
- C++ 如何重载 [] 运算符并进行函数调用
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 为什么我可以在不重载 "=" 运算符的情况下将一个对象分配给另一个对象?
- 重载运算符有地址吗?
- 如何迭代重载运算符 [] 的类?
- 重载运算符与添加问题
- 模板基类中的重载运算符
- 如何调用用于重载运算符"<<"的 friend 函数?
- 在 C++17 中的命名空间和子命名空间中重载运算符是不明确的
- 重载运算符<<采用谷歌 C++ 风格
- C++ 如何正确重载 + 运算符
- cout (<<) 重载运算符不打印减去的矩阵
- 如何在 c++ 中重载运算符 + 以便能够 whrite c_str = "smth" + c_str;
- 重载运算符*以获取对另一个类的实例的引用