5

当我回答这个问题时出现了这个问题:标准是否允许并保证friend-ing 标准库类和/或函数?

在这种特殊情况下,问题是:

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

private:
    ~MyUserDefinedType() { }
}

保证允许使用默认删除器MyUserDefinedType存储在std::unique_ptr<MyUserDefinedType>orstd::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

4

2 回答 2

5

是否可能std::default_delete<MyUserDefinedType>实际上是在 std 的内部命名空间中定义的类的 using 别名,在这种情况下,朋友声明将是非法的?

没有。根据 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 效果:呼吁deleteptr

4 备注:如果T是不完整类型,则程序格式错误。

这仅指定在函子上调用调用运算符的效果default_delete<>应该是什么,而不是具体如何实现(无论是直接在调用运算符的主体内,还是通过将任务委托给某个其他类的某个成员函数)。

于 2013-03-04T07:29:02.570 回答
1

一般来说,标准库中描述的类是否需要直接实现其功能,或者它们可以使用任意级别的间接?

通常,实现可以根据需要间接实现。例如,查看标准容器及其迭代器的实现——或者只是错误地使用它们,然后从错误消息中查看涉及哪些模板。但是,既然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!
}

朋友可以对你做任何事。谨慎选择它们。在这种情况下,我真的推荐一个自定义删除器 - 假设将析构函数设为私有但通过删除器提供对它的访问是有意义的。

于 2013-03-04T07:47:06.483 回答