简体   繁体   English

运算符如何在名称空间中重载解析?

[英]How does the operator overload resolution work within namespaces?

I found a strange behaviour of C++ resolution of operator-overloading, I can't explain myself. 我发现C ++解析运算符重载的奇怪行为,我无法解释自己。 A pointer to some resource describing it would be just as nice as an answer. 指向描述它的某个资源的指针就像答案一样好。

I have 2 translation units. 我有2个翻译单位。 In one (called util.cpp/h) I declare and define two operators (I omit the real implementations for readabilty, the problam occurs anyway): 在一个(称为util.cpp / h)中,我声明并定义了两个运算符(我省略了readabilty的实际实现,无论如何都会发生problam):

// util.h
#ifndef GUARD_UTIL
#define GUARD_UTIL

#include <iostream>

std::istream& operator>>(std::istream& is, const char* str);
std::istream& operator>>(std::istream& is, char* str);
#endif

And: 和:

//util.cpp
#include "util.h"
#include <iostream>

std::istream& operator>>(std::istream& is, const char* str) {
  return is;  
}
std::istream& operator>>(std::istream& is, char* str) {
  return is;  
}

These operators are, if course in global namespace, since they operate on std types and built-in types and should be usable from everywhere. 如果在全局命名空间中,这些运算符是因为它们在std类型和内置类型上运行,并且应该可以在任何地方使用。 They just work fine from global namespace (eg from main()) or with explicitly telling the compiler that they are in global namespace (see code example). 它们只能从全局命名空间(例如来自main())或明确地告诉编译器它们在全局命名空间中工作(参见代码示例)。

In another translation unit (called test.cpp/h) I use these operators within a namespace. 在另一个翻译单元(称为test.cpp / h)中,我在命名空间中使用这些运算符。 This works until I put a similar operator into this namespace. 这有效,直到我将类似的运算符放入此命名空间。 As soon as this operator is added, the compiler (eg gcc or clang) is not able to find a viable operator>> anymore. 一旦添加此运算符,编译器(例如gcc或clang)就不能再找到可行的运算符>>。

// test.h
#ifndef GUARD_TEST
#define GUARD_TEST

#include <iostream>

namespace Namespace {
  class SomeClass {   
    public:
      void test(std::istream& is);
  };

  // without the following line everything compiles just fine
  std::istream& operator>>(std::istream& is, SomeClass& obj) { return is; }; 
}

#endif

And: 和:

//test.cpp
#include "test.h"
#include "util.h"
#include <iostream>

void Namespace::SomeClass::test(std::istream& is) {
  ::operator>>(is, "c"); //works
  is >> "c" //fails
}

Why does the compiler find the correct operator when there is no operator>> in Namespace but fails to find when there is one? 为什么编译器在命名空间中没有operator >>但是找不到时会找到正确的运算符? Why does the operator affect the ability of the compiler to find the correct one even if it has a different signature? 为什么运算符会影响编译器找到正确的编译器的能力,即使它具有不同的签名?

One attempt to fix this was to put 解决这个问题的一个尝试是

std::istream& operator>>(std::istream& is, const char* str) { ::operator>>(is, str); std :: istream&operator >>(std :: istream&is,const char * str){:: operator >>(is,str); } }

into Namespace, but than the linker complains about previous definitions. 进入命名空间,但链接器抱怨以前的定义。 So additional: Why can the linker find something the compiler doesn't find? 附加:为什么链接器会找到编译器找不到的东西?

This is a name hiding issue. 这是一个隐藏名称的问题。 The standard says (c++03, 3.3.7/1) 标准说(c ++ 03,3.3.7 / 1)

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2). 名称可以通过嵌套声明性区域或派生类(10.2)中相同名称的显式声明来隐藏。

The "name" on your case would be operator>> and namespaces constitute nested declarative regions. 您的案例中的“名称”将是operator>> ,名称空间构成嵌套的声明性区域。

The easiest way to fix that would be to use a using declaration where you declare the namespace-local operator<< : 解决这个问题的最简单方法是使用using声明using声明namespace-local operator<<

namespace your_namespece {
    std::istream& operator>>(std::istream& is, SomeClass& obj) { return is; }; 
    using ::operator>>;
}

Note that this feature doesn't interfere with Koenig lookup (at least in your case, in principle, it can), so IO operators from std:: will still be found. 请注意,此功能不会干扰Koenig查找(至少在您的情况下,原则上它可以),因此仍然可以找到std:: IO运算符。

PS: Another possibility for working aroud this issue would be defining the operator for SomeClass as an inline friend . PS:另一种解决这个问题的可能性是将SomeClass的运算符定义为内联friend Such functions are declared at the namespace level (outside of "their" class), but are not visible from there. 这些函数在命名空间级别(在“他们的”类之外)声明,但从那里看不到。 They can only be found by Koenig lookup. 它们只能通过Koenig查找找到。

There are several issues here; 这里有几个问题; for starters, you're redefining a function in global namespace that already exists in std:: . 对于初学者,您正在重新定义std::中已存在的全局命名空间中的函数。 The problem you describe, however, is due to the way name lookup works. 但是,您描述的问题是由于名称查找的工作方式。 Basically, in the case of operator overloading, the compiler does two name lookups. 基本上,在运算符重载的情况下,编译器会执行两次名称查找。 The first (used for all symbols, not just operators) starts at the scope where the symbol appears, and works outwards: first the local blocks, then the class, and its base classes (if any), and finally the namespaces, working out to the global namespace. 第一个(用于所有符号,不仅仅是运算符)从符号出现的范围开始,向外工作:首先是本地块,然后是类,它的基类(如果有的话),最后是名称空间,到全局命名空间。 An important characteristic of this lookup is that it stops in whatever scope it finds the name: if it finds a name in local scope, it doesn't look in any classes; 这种查找的一个重要特征是它在它找到名称的任何范围内停止:如果它在本地范围内找到一个名称,它就不会查找任何类; if it finds one in a class, it doesn't look in the base classes or namespaces, and if it finds one in a namespace, it doesn't look in any enclosing namespaces. 如果它在类中找到一个,它不会查找基类或命名空间,如果它在命名空间中找到一个,则它不会查找任何封闭的命名空间。 As far as this lookup is concerned, all overloads must be in the same scope. 就此查询而言,所有重载必须在同一范围内。 The second lookup only affects functions and operator overloads, and occurs in the context of classes or objects used as arguments; 第二个查找仅影响函数和运算符重载,并发生在用作参数的类或对象的上下文中; thus, if one of the operands is a class in the standard library (or anything derived from a class in the standard library), the compiler will look for functions in std:: , even though the context where the symbol is used doesn't include std:: . 因此,如果其中一个操作数是标准库中的一个类(或从标准库中的类派生的任何内容),编译器将在std::查找函数,即使使用该符号的上下文不包括std:: The problem you're having is that built-in types, like char* , don't imply any namespace (not even global): given your overloads, the first lookup will stop at the first operator>> it sees, and the second will only look in std:: . 您遇到的问题是内置类型(如char* )并不暗示任何命名空间(甚至不是全局):给定您的重载,第一个查找将停在第一个operator>>它看到,第二个查找将停止只会查看std:: Your function is in neither. 你的功能都不是。 If you want an overloaded operator to be found, you must define it in the scope of one of its operands. 如果要查找重载运算符,则必须在其某个操作数的范围内定义它。

Concretely, here: you can't overload std::istream& operator>>( std::istream&, char* ) , because it is already overloaded in the standard library. 具体来说,这里:你不能重载std::istream& operator>>( std::istream&, char* ) ,因为它已经在标准库中重载了。 std::istream& operator>>( std::istream&, char const* ) is possible, but I'm not sure what it's supposed to do, since it can't write to the second operand. std::istream& operator>>( std::istream&, char const* )是可能的,但我不确定它应该做什么,因为它无法写入第二个操作数。 More generally, you should only overload this operator for types that you have defined, and you should put your overload in the same namespace as the type itself, so that it will be found by the second lookup above (called Argument Dependent Lookup, or ADL—or earlier, Koenig lookup, after the person who invented it). 更一般地说,您应该只为您定义的类型重载此运算符,并且您应该将重载放在与类型本身相同的命名空间中,以便通过上面的第二个查找找到它(称为参数依赖查找或ADL) - 或更早,Koenig查找,发明它的人之后)。

:: is global scope, so, compiler must scan global namespace and find this operator. ::是全局范围,因此,编译器必须扫描全局命名空间并找到此运算符。 is >> "C", trying to find operator >> in Namespace, so, compiler find it and stop searching, then compiler try to choose operator with needed signature, if there is no such operator - compiler fails. 是>>“C”,试图在Namespace中找到operator >>,所以,编译器找到并停止搜索,然后编译器尝试选择需要签名的运算符,如果没有这样的运算符 - 编译器失败。 I think you should read Herb Sutter Exceptional C++. 我想你应该阅读Herb Sutter Exceptional C ++。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 运算符重载解析在名称空间内工作 - Operator overload resolution work within namespaces 在新的初始化序列存在的情况下,运算符重载解析如何工作? - How does operator overload resolution work in the presence of the new initializer sequence? 重载解析如何与可变参数函数一起使用? - How does overload resolution work with variadic functions? 在此示例中,赋值运算符重载解析如何工作? 结果对我来说意外 - How does the assignment operator overload resolution work in this example? The result is unexpected for me 如何在以下c ++代码中基于返回类型进行运算符重载解析 - how does operator overload resolution work based on return type in the following code of c++ 查找名称空间的重载解析 - Overload resolution looking into namespaces 插入运算符 (operator&lt;&lt;) 的这种递归重载是如何工作的? - How does this recursive overload of the insertion operator (operator<<) work? 我可以重载一个重载的运算符吗? 操作员重载如何工作 - Can I overload an overloaded operator? How does Operator Overloading work 此模板类型推导和重载解析如何工作? - How does this template type deduction and overload resolution work? 重载解析如何在private修饰符的上下文中起作用? - How does overload resolution work in the context of private modifier?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM