如何显式调用命名空间限定的析构函数? [英] How to explicitly call a namespace-qualified destructor?
问题描述
我很惊讶以下简单代码无法编译(使用gcc,版本4.8.1)
#include < string>
void test()
{
std :: string * p =新的std :: string(销毁我);
p-> std ::〜string();
}
它说:错误:作用域'std'在'〜'之前不是班级名称。但是阅读该标准后,我会说语法应该是 后缀表达式-> 伪构造函数名称,其中 pseudo-constructor-name 的格式可以为 嵌套名称说明符〜类型名称,而嵌套名称说明符可以为 identifier ::。
如果省略 std ::,则会导致投诉称在左括号之前应输入类名,并将其放在波浪号之后,以抱怨在 ::之前需要一个类名。经过一番尝试后,我发现它将在编写 p-> std :: string ::〜string();
时进行编译(但是当编写 p-> std :: string ::〜std :: string();
代替)。但是用自己的类型名来限定析构函数并不是一个中立的操作;我从标准的12.4:13中的示例中收集到(但奇怪的是,不是从规范文本中得出的),这迫使要调用确切的静态(基)类的析构函数,而不是作为(最派生的)虚函数。指向的实际对象的类型。在这里没有什么区别,但在类似情况下会有所不同。为什么语法会强制只使用静态类型?
但是,使用clang而不是gcc时,即使上述变量也会产生语法错误。但是,如果您在阅读错误消息时出于这种幽默的目的,则clang的错误消息会更有趣:for p-> std :: string ::〜string();
它给出预期在〜后面的类名来命名析构函数(确实如此;一个人想知道哪种类型的类名如果以波浪号开头则不会命名析构函数),并且对于我的初次试用 p-> std ::〜string()
它反驳了合格的成员访问是指名称空间'std'中的成员(再一次想知道哪里出了问题这样做;实际上,要调用的析构函数位于名称空间 std中)。我已经尝试了所有8种合理的组合(在波浪号之前的std ::和/或string ::和/或std ::之后的),但都没有用clang编译。
即使使用clang,我也可以使用std :: string; 使用对其进行编译。但是我感到奇怪的是,我在标准中没有发现这样的声明是必需的,实际上我找不到任何解决调用命名空间限定类的析构函数的问题。 。最后一点,我想补充一点,我感到奇怪的是,在调用时必须使用命名空间限定符破坏者。由于这是来自指定对象(此处为
* p
)的成员访问,因此不依赖于参数的查找是否不必明确限定名称空间?
在标准中,位于:
§3.4 .5 / 3
如果unqualified-id是〜type-name,则在整个后缀表达式的上下文中查找类型名。
因此,似乎应该在〜string
中查找 std ::
命名空间的上下文。
实际上,考虑到相应的自制版本的工作方式如下在GCC和Clang上:
命名空间STD {
class STRING {};
}
int main(){
STD :: STRING * a = new STD :: STRING();
a->〜STRING();
}
我会继续说这很可能是一个错误。
< hr>
显然,考虑到 std :: string
实际上是 std :: basic_string< char>
,如果您致电:
a->〜basic_string();
然后一切都可以编译。
我仍然认为这是一个bug,考虑到以下示例(取自标准)显示 typedef
s也应该起作用:
struct B {
virtual〜B(){}
};
结构D:B {
〜D(){}
};
D D_object;
typedef B B_alias;
B * B_ptr =& D_object;
void f(){
D_object.B ::〜B();
B_ptr->〜B();
B_ptr->〜B_alias();
B_ptr-> B_alias ::〜B();
B_ptr-> B_alias ::〜B_alias();
}
此概念与§3.4.5/ 3一起应保证: / p>
p->〜string();
应该可以。
I am surprised that the following simple code won't compile (with gcc, version 4.8.1)
#include <string>
void test()
{
std::string* p = new std::string("Destruct me");
p->std::~string();
}
It says: error: scope ‘std’ before ‘~’ is not a class-name. Yet reading the standard, I would say the syntax says it should be "postfix-expresssion -> pseudo-constructor-name", where pseudo-constructor-name can be of the form "nested-name-specifier ~ type-name", and nested-name-specifier can be "identifier::".
Leaving out "std::" leads to the complaint that a class name was expected before the left paren, and putting it after the tilde to the complaint that a class name was expected before "::". After some trying I found that it will compile when written p->std::string::~string();
(but not when one writes p->std::string::~std::string();
instead). But qualifying the destructor with its own type name is not a neutral operation; I gather from the example in 12.4:13 of the standard (but curiously not from the normative text) that this forces the destructor of the exact static (base) class to be called, rather than as a virtual function that of (the most derived type of) the actual object pointed to. Here it makes no difference, but in similar cases it would; why would the syntax force exclusively using the static type?
However, with clang instead of gcc even the mentioned variant gives a syntax error. The error messages of clang are more amusing though, if you're in the mood for for this kind of humour when reading error messages: for p->std::string::~string();
it gives "expected the class name after '~' to name a destructor" (and so it does; one wonders which kind of class names would not name a destructor if prefixed by a tilde), and for my initial trial p->std::~string()
it retorts "qualified member access refers to a member in namespace 'std'" (again one wonders what is wrong with that; indeed the destructor to be called lives in the namespace 'std'). I've tried all 8 reasonable combinations (std:: and/or string:: before the tilde, and/or std:: after it) and none of them compile with clang.
I can make it compile, even with clang, using using std::string;
. But what I find curious is that I can find no indication in the standard that such a declaration was intended to be necessary in such cases.In fact I can find nothing that addresses the issue of calling the destructor of a namespace-qualified class at all. Am I missing something obvious?
As a final note, I'd like to add that it strikes me as odd that one have to use a namespace qualification at all when calling a destructor. Since this is member access from a well specified object (here *p
) shouldn't argument-dependent lookup make explicitly qualifying the namespace unnecessary?
In the standard, at:
§3.4.5/3
If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression.
therefore it would seem that ~string
should be looked up in the context of the std::
namespace.
In fact, considering that a corresponding home-made version works as follows on both GCC and Clang:
namespace STD {
class STRING {};
}
int main() {
STD::STRING* a = new STD::STRING();
a->~STRING();
}
Live demo with clang++ Live demo with g++
I'll go ahead and say this is most likely a bug.
Apparently, given that std::string
is really std::basic_string<char>
if you call:
a->~basic_string();
Live demo with clang++ Live demo with g++
then everything compiles fine.
I still remain of the idea that this a bug, considering that the following example (taken from the standard), shows that typedef
s should also work:
struct B {
virtual ~B() { }
};
struct D : B {
~D() { }
};
D D_object;
typedef B B_alias;
B* B_ptr = &D_object;
void f() {
D_object.B::~B();
B_ptr->~B();
B_ptr->~B_alias();
B_ptr->B_alias::~B();
B_ptr->B_alias::~B_alias();
}
This notion, together with §3.4.5/3 should guarantee that:
p->~string();
should work.
这篇关于如何显式调用命名空间限定的析构函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!