2013-07-19 15 views
22

Kod STL, który czytam może być stary ... ale pytanie jest bardziej związane z gramatyką szablonu C++.C++ std :: destroy (T * wskaźnik)

Pytanie otacza tę stl szablonu funkcję:

template<class T> std::destroy(T *p) { 
    p->~T(); 
} 

I nie wydają się znaleźć specjalizacji funkcji std :: zniszczyć (T *). Wydaje mi się więc, że funkcja szablonu będzie tworzyła to samo dla typów "int" i wywoływała destruktor "int". Aby o tym mówić, stworzyłem ten przykładowy kod emulujący std :: destroy. Nazywam to my_destroy ih the example.

#include <iostream> 
#include <stdio.h> 
using namespace std; 

template <class T> 
void my_destroy(T * pointer) { 
    pointer->~T(); 
} 
int main() 
{ 
    int *a; 
    //a->~int();  // !!! This won't compile. 
    my_destroy<int>(a); // !!! This compiles and runs. 
} 

}

Ku mojemu zaskoczeniu, linia ta nie kompiluje:

a->~int(); 

Ale ta linia kompiluje:

my_destroy<int>(a); 

Moja dezorientacja jest, myślałem, że my_destroy<int>(a) zostanie utworzony jako odpowiednik a->~int();

Na pytanie w większym kontekście, gdy pojemnik STL z <int> wymazuje element, jak działa std::destroy()?

+0

Nie wiem, dlaczego jest to odrzucane, jest to uzasadnione pytanie. +1 – Rapptz

+4

To się nazywa pseudo-destruktor i ma własne, specjalne zasady. Zasadniczo można użyć tylko * nazwy typu *, która jest nazwą klasy/enum lub nazwą typef. – dyp

+1

To, że nie można użyć słowa kluczowego do oznaczenia połączenia z destruktorem. 'typedef int INT; a-> ~ INT(); 'kompiluje. – milleniumbug

Odpowiedz

3

Język pozwala na to, aby włączyć ogólne programowanie. Jednak twoje "proste" wywołanie nie jest w ogólnym kodzie, a więc nie działa.

Kolejny taki przypadek jest

template <typename T> void foo() 
{ 
    T instance = T(); // default constructor 
} 

foo<int>(); // will work 

Więc tak:

template <typename T> void foo() 
{ 
    T* instance = new T(); // default constructor 
    delete instance;  
} 

będzie również pracować dla wbudowanego prymitywnych typów, ponieważ T jest typename w zakresie ot szablonu.

+0

'int instance = int();' działa dobrze. –

+0

@MooingDuck hmmm. Dzieje się tak, ponieważ istnieje różnica między 'int instancją;' i 'int instancją = int();' - ostatnia wartość - inicjuje ją, podczas gdy pierwsza pozostawia ją niezainicjowaną. – sehe

32

zauważyć, że podczas gdy a->~int(); nie kompiluje, to robi:

typedef int INT; 
int* a; 
a->~INT(); 

od standardu:

5.2.4p1 Zastosowanie pseudo-destructor-name po kropce. lub strzałka -> operator reprezentuje destruktor dla typu nieklasowanego oznaczonego przez type-name lub decltype-specifier. Wynik zostanie użyty jako operand dla operatora wywołania funkcji(), a wynik takiego wywołania ma typ void. Jedynym efektem jest ocena postfiksowego wyrażenia przed kropką lub strzałką.

Od 5.2p1:

pseudo-destructor-name: 
    nested-name-specifier_opt type-name :: ~ type-name 
    nested-name-specifier template simple-template-id :: ~ type-name 
    nested-name-specifier_opt~ type-name 
    ~ decltype-specifier 

I wreszcie, 7.1.6.2p1:

type-name: 
    class-name 
    enum-name 
    typedef-name 
    simple-template-id 

Więc, co ciekawe, nie jest składniowo inttype-name (jest to simple-type-specifier) i tak można” t zadzwoń pod numer ~int(), ale INT jest i możesz.

+0

+1 za odpowiedź na pytanie – Rapptz

+0

tutaj, moje ostateczne głosowanie na ten dzień. Nauczyłem się czegoś! – TemplateRex

4

Na klasami (ów nieskalarnych), wyrażenie

pointer->~T(); 

zasadniczo wywołanie funkcji (a przyrostek ekspresja). Aby zidentyfikować funkcję, która ma zostać wywołana, należy przeanalizować część pointer->~T. Numer ~T jest id-expression IFF T jest nazwą klasy , identyfikującą funkcję destruktora.

Oczywiście, int nie jest nazwą klasy. Ale jeśli T to nazwa typu skalarnego, to samo wyrażenie jest przetwarzane inaczej. Cała część pointer->~T jest identyfikowana jako specjalna Postfix-ekspresja nazywana pseudo-destruktorem o nazwie o nazwie. Z następującym () wyrażenie jest uznawane za wywołanie pseudo-destruktora (reguły w [expr.pseudo] zabraniają wykonywać cokolwiek innego za pomocą nazwy pseudo-destruktora, ale wywoływać ją).

sam int nie jest typu nazwa [dcl.type.simple], ale prosty typu specyfikator:

typu nazwa:

  • wykładowa nazwa
  • nazwa-enka
  • nazwa typefa
  • prosty szablon id

Dlatego można użyć typedef byłyby int lub T jak w przykładzie (*), ale nie int bezpośrednio. Rozumowanie zostało dobrze wyjaśnione przez sehe.

Reguły dla wywołań pseudo-destruktora są określone w [wyrażenie.pseudo]: "Jedynym skutkiem jest ocena postfiksów przed kropką lub strzałką."

(*) z [temp.param]/3 „a typu parametru których identyfikatory nie następstwem elipsa określa jego identyfikator być typedef nazwa [...]”

+0

+1 za doskonały wygląd. Właśnie nazwałem to tak, jak to widzę. Być może jestem trochę zbyt pragmatyczny, by przywołać standardowego, ale jest to z pewnością pouczające – sehe