2013-03-04 3 views
5

Этот вопрос возник, как я ответил this question: позволяет ли стандарт разрешать и делать какие-либо гарантии относительно friend -учет стандартных классов библиотек и/или функций?Дружественные классы, определенные в пространстве имен std: любые гарантии?

В данном конкретном случае, ситуация вопрос, был ли:

class MyUserDefinedType 
{ 
    friend struct std::default_delete<MyUserDefinedType>; 

private: 
    ~MyUserDefinedType() { } 
} 

гарантированно позволит MyUserDefinedType хранить в std::unique_ptr<MyUserDefinedType> или std::shared_ptr<MyUserDefinedType> объекта с Deleter по умолчанию.

В общем, классы, описанные в стандартной библиотеке, необходимые для реализации их функциональности напрямую или могут использовать любой произвольный уровень косвенности? Например, это возможно, что

  • std::default_delete<MyUserDefinedType> на самом деле является using псевдоним класса, определенного во внутреннем пространстве имен std, и в этом случае friend декларация будет незаконно

или

  • std::default_delete<MyUserDefinedType> вызывает некоторый другой класс, который фактически выполняет удаление, и в этом случае объявление friend не будет иметь желаемого ef fect

или что-то еще вдоль этих линий?

Моя догадка заключается в том, что это UB не гарантированно работает, но мне любопытно, относится ли это конкретно к стандарту.

Этот конкретный пример, приведенный выше работ по стволу Clang (ж/LibC++) и GCC 4.7.2 (ж/libstdC++), FWIW

+0

Я не знаю ответа, но «подружиться» 'std :: default_delete <>' будет, если он работает, позволяет почти любому вызвать деструктор класса. Это заставляет меня задаться вопросом, почему сделать деструктор приватным в первую очередь, если затем используется объявление друга, которое делает его (почти) общедоступным. (По общему признанию, это все равно означает, что объекты автоматического хранилища не могут быть выделены.) – jogojapan

+0

Я бы не ожидал, что это будет правда (хотя я прекратил быть экспертом на C++ десять лет назад); экспериментально, похоже, это не относится к последним g ++ и vC++, FWIW. –

+0

Я не думал, что решение было хорошим, я предложил реализовать новый deleter –

ответ

5

возможно, что std::default_delete<MyUserDefinedType> на самом деле, используя псевдоним класса определен во внутреннем пространстве имен std, и в этом случае объявление друга было бы незаконным?

No. В соответствии с пунктом 20.7.1.1.2 на C++ 11 Стандарт на:

namespace std { 
    template <class T> struct default_delete { 
     constexpr default_delete() noexcept = default; 
     template <class U> default_delete(const default_delete<U>&) noexcept; 
     void operator()(T*) const; 
    }; 
} 

Тот факт, что он должен быть шаблоном класса явно указан. Это означает, что он не может быть шаблоном псевдонимов. Если бы это было так, было бы невозможно специализироваться на этом.

Возможно ли, что std::default_delete<MyUserDefinedType> вызывает другой класс, который фактически выполняет удаление, и в этом случае объявление друга не будет иметь желаемого эффекта?

Да. Ничто в стандарте не указывает, что вызов не может быть выполнен каким-либо внутренним помощником. В пункте 20.1.1.2:

void operator()(T *ptr) const; 

3 Эффекты: вызовы delete на ptr.

4 Примечания: Если T является неполным, программа плохо сформирована.

Это только указывает, что эффект от вызова оператора вызова на default_delete<> функтора должно быть, не как это должно быть достигнуто конкретно (непосредственно в теле оператора вызова, или путем передачи задача к некоторой функции-члену другого класса).

+0

ваш ответ для второй части имеет смысл для меня, но знаете ли вы, что эта проблема специально адресована где угодно? явно ли UB дружить стандартным классом или просто неявно? –

+0

@StephenLin: Почему это должно быть UB? Я просто считаю это переносным решением, потому что вызов 'delete' * может *, но не должен выполняться самим классом' default_delete'. –

+0

ну, ок, я имею в виду, это поведение, которое изменяет семантику программы (например, что она может или не может компилироваться), и она не определена ... возможно, UB слишком силен, но что еще это будет? неопределенные? –

1

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

В общий реализация может косвенно столько, сколько она хочет. Имейте, например, посмотрите на реализации стандартных контейнеров и их итераторов - или просто используйте их неправильно и посмотрите, какие шаблоны задействованы в сообщениях об ошибках. Однако, поскольку default_delete не является чем-то волшебным, это должно быть однострочным, вы можете ожидать, что он сам выполнит эту работу, но это не гарантировано.

Я предполагаю, что это UB, но мне любопытно, если это конкретно рассматривается стандартом.

Это не UB, это просто неуказано.

Вы можете быть уверены, что если вы просто специализированы default_delete<MyUserDefinedType> (это является позволило специализировать стандартные шаблоны libraray), но я бы не сделать это.

Я бы не использовал дружбу вообще, особенно если дело касается шаблонов, которые не были специализированы. Рассмотрим это:

//your code 
class MyUserDefinedType 
{ 
    friend struct std::default_delete<MyUserDefinedType>; //for deletion 
private: 
    int veryPrivateData; 
    ~MyUserDefinedType() { } 
}; 

//evil colleague's code: 
namespace std { 
    //we may specialize std-templates for UDTs... 
    template<> 
    struct default_delete<MyUserDefinedType> 
    { 
    constexpr default_delete() noexcept = default; 
    template <class U> default_delete(const default_delete<U>&) noexcept {} 
    void operator()(T* pt) const { delete pt; } 

    //sneaky... 
    void access(MyUserDefinedType& mudt, int i) const 
    { mudt.veryPrivateData = i; } 
    }; 
} 

void somewhere_deep_in_the_code() 
{ 
    MyUserDefinedType& myUDT = /*something...*/; 
    std::default_delete<MyUserDefinedType>().access(myUDT, 42); //tricked you! 
} 

Друзья могут сделать что-нибудь к вам. Выбирайте их с осторожностью. В этом случае я бы порекомендовал пользовательский отладчик - если предположить, что это делает любой деструктор приватным, но обеспечивает доступ к нему через делетера.

+0

Да, я предложил пользовательский делетер, это n от моего рекомендованного решения –

+0

+1 спасибо за ввод, но я все еще надеюсь, что кто-то может найти очень авторитетную цитату о «обычных» классах/функциях –