Подтвердить что ты не робот

Как явным образом вызывать деструктор, подходящий для пространства имен?

Я удивлен, что следующий простой код не будет компилироваться (с gcc, версия 4.8.1)

#include <string>
void test()
{
  std::string* p = new std::string("Destruct me");
  p->std::~string();
}

В нем говорится: error: scope 'std before' ~ не является именем класса. Однако, прочитав стандарт, я бы сказал, что синтаксис говорит, что это должно быть "postfix-expresssion → псевдо-конструктор-имя", где псевдоконструктор-имя может иметь форму "имя-имя-спецификатор-type-name", и вложенным именем-спецификатором может быть "идентификатор".

Оставшаяся "std::" приводит к жалобе на то, что перед левым парнем ожидалось имя класса, и после того, как тильда подала жалобу на то, что имя класса ожидалось до "::". После некоторых попыток я обнаружил, что он скомпилируется при записи p->std::string::~string(); (но не при записи p->std::string::~std::string();). Но квалификация деструктора с собственным типом имен не является нейтральной операцией; Я собираюсь из примера в 12.4: 13 стандартного (но с любопытством не из нормативного текста), что это заставляет деструктор точного статического (базового) класса вызываться, а не как виртуальную функцию, тип) фактического объекта, на который указывает. Здесь это не имеет значения, но в подобных случаях это было бы; почему синтаксическая сила использует исключительно статический тип?

Однако с clang вместо gcc даже упомянутый вариант дает синтаксическую ошибку. Сообщения об ошибках clang более забавные, хотя, если вы настроены на такой юмор при чтении сообщений об ошибках: для p->std::string::~string(); он дает "ожидаемое имя класса после" ~ ", чтобы назвать деструктор" (и поэтому он задается вопросом: какие типы имен классов не будут называть деструктор, если префикс тильды), а для моего начального испытания p->std::~string() он реплицирует "квалифицированный доступ к члену" относится к члену в пространстве имен "std" (снова один задается вопросом, что с этим не так, ведь деструктор, который будет называться, живет в пространстве имен "std" ). Я пробовал все 8 разумных комбинаций (std:: и/или string:: перед тильдой, и/или std:: после него), и ни одна из них не компилируется с clang.

Я могу скомпилировать его, даже с clang, используя using std::string;. Но то, что мне кажется любопытным, заключается в том, что я не могу найти в стандарте никаких указаний о том, что такое объявление должно было быть необходимо в таких случаях. На самом деле я не могу найти ничего, что могло бы решить проблему вызова деструктора класса, Мне что-то не хватает?

В качестве заключительной заметки я хотел бы добавить, что мне кажется странным, что при вызове деструктора нужно использовать квалификацию пространства имен. Поскольку это членский доступ из хорошо определенного объекта (здесь *p), не должен зависящий от аргумента поиск явно запрещает ненужное пространство имен?

4b9b3361

Ответ 1

В стандарте:

§3.4.5/3

Если unqualified-id - это имя типа, имя типа просматривается в контексте всего постфиксного выражения.

поэтому кажется, что ~string следует искать в контексте пространства имен std::.

Фактически, учитывая, что соответствующая домашняя версия работает как на GCC, так и на Clang:

namespace STD {
class STRING {};
}

int main() {
    STD::STRING* a = new STD::STRING();
    a->~STRING();
}

Live demo with clang++ Live demo with g++

Я продолжу и скажу, что это скорее всего ошибка.


По-видимому, учитывая, что std::string действительно std::basic_string<char>, если вы вызываете:

a->~basic_string();

Live demo with clang++ Live demo with g++

тогда все компилируется отлично.

Я до сих пор не знаю, что это ошибка, учитывая, что следующий пример (взятый из стандарта) показывает, что typedef также должен работать:

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();
}

Это понятие вместе с §3.4.5/3 должно гарантировать, что:

p->~string();

должен работать.

Ответ 2

Обновление 2019: начиная с C++ 17, вы можете использовать std::destroy_at следующим образом:

std::destroy_at(p);

Это намного проще и следует принципу не использовать "примитивные конструкции" (такие как выражения new/delete) в современном C++.