当我回答this question时,出现了这个问题:标准是否允许并对friend
标准库类和/或函数做出任何保证?
在这种特殊情况下,问题的关键是:
class MyUserDefinedType
{
friend struct std::default_delete<MyUserDefinedType>;
private:
~MyUserDefinedType() { }
}
保证允许MyUserDefinedType
使用默认删除器存储在std::unique_ptr<MyUserDefinedType>
或std::shared_ptr<MyUserDefinedType>
对象中。
通常,标准库中描述的类是直接实现其功能所必需的,还是可以使用任意级别的间接?例如,是否有可能
std::default_delete<MyUserDefinedType>
实际上是using
内部命名空间中定义的类的std
别名,在这种情况下friend
声明是非法的或
std::default_delete<MyUserDefinedType>
调用其他实际执行删除的类,在这种情况下,friend
声明不会产生预期效果或其他类似的东西?
我的猜测是 UB 无法保证可以正常工作,但我很好奇这是否是标准专门解决的问题。
上面给出的这个具体例子适用于clang trunk(w / libc ++)和GCC 4.7.2(w / libstdc ++),FWIW
答案 0 :(得分:5)
有可能
std::default_delete<MyUserDefinedType>
实际上是std内部命名空间中定义的类的别名,在这种情况下,friend声明是非法的吗?
否即可。根据C ++ 11标准的第20.7.1.1.2段:
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<>
仿函数上的调用操作符的效果应该是什么,而不是如何具体实现(无论是直接在内部)调用操作符的主体,或者通过将任务委托给某个其他类的某个成员函数来实现。)
答案 1 :(得分:1)
通常,标准库中描述的类是直接实现其功能所必需的,还是可以使用任意级别的间接?
在 general 中,实现可以根据需要进行间接实现。有例如查看标准容器及其迭代器的实现 - 或者只是错误地使用它们,并查看错误消息中涉及的模板。但是,由于default_delete
不是什么神奇的东西,它应该是一个单行,你可以期待它自己完成工作,但不能保证。
我的猜测是这是UB,但我很好奇,如果这是由标准专门解决的。
这不是UB,它只是未指明。
你可以确定你是否只是专门的default_delete<MyUserDefinedType>
( 允许专门化标准的图书馆模板),但我不会这样做。
我根本不会使用友谊,特别是如果涉及到没有专门化的模板的话。考虑一下:
//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!
}
朋友可以为你做任何事情。请谨慎选择。在这种情况下,我真的推荐一个自定义删除器 - 假设它有意义使析构函数私有,但通过删除器提供对它的访问。