使std :: unique <t>与std :: unique <const t,=“”customdeletertype =“”>兼容

时间:2015-10-07 13:55:54

标签: c++ c++11

在代码中我为特定对象定义了3个std :: unique_ptr指针类型:

typedef std::unique_ptr<MyObject> nonConstPtrDefaultDelete;

typedef std::unique_ptr<MyObject, std::function<void(MyObject *)>>
                                                       nonConstPtrCustomDelete;

typedef std::unique_ptr<const MyObject, std::function<void(const MyObject *)>>
                                                       ConstPtrCustomDelete;

我遇到了一个用例,我需要将nonConstPtrDefaultDelete转换为ConstPtrCustomDelete,将nonConstPtrCustomDelete转换为ConstPtrCustomDelete。 换句话说:

nonConstPtrDefaultDelete a;
nonConstPtrCustomDelete b;

ConstPtrCustomDelete c1(a);  // Compiler error Deleter has incompatible type
ConstPtrCustomDelete c2(b);  // Compiler error Deleter has incompatible type

主要问题来自删除功能的类型签名不兼容。可以通过以下方式更改nonConstPtrCustomDelete类型的定义来修复nonConstPtrCustomDelete案例:

typedef std::unique_ptr<MyObject, std::function<void(const MyObject *)>>
                                                         nonConstPtrCustomDelete

但是,最常见的DefaultDelete情况仍然会产生编译错误,尽管直观地说明转换是可能的。 它有一种解决方法,限制并提示编译器的功能可以从一个转换为另一个吗?

谢谢

4 个答案:

答案 0 :(得分:6)

如果你确定删除器是正确的,你可以将DefaultDelete转换为你的类型:

nonConstPtrDefaultDelete a;
ConstPtrCustomDelete c1( a.release(), your_deleter );

与const / non const版本相同。但是为什么你需要2个版本(一个用于const而一个用于not)尚不清楚。

答案 1 :(得分:4)

给出的例子,

nonConstPtrDefaultDelete a;
nonConstPtrCustomDelete b;

ConstPtrCustomDelete c1(a);  // Compiler error Deleter has incompatible type
ConstPtrCustomDelete c2(b);  // Compiler error Deleter has incompatible type

......有两个问题:

  • 您无法从左值表达式构造unique_ptr。它是单一所有权,但左值表达式要求提供副本。

  • 对于删除者,函数类型void(T*)与函数类型void(T const*)不兼容。

当指针类型为void(T const*)时,需要T const*删除函数,并且当指针类型为T*时也可以正常工作。因此,无视既定惯例,这将是最实际的惯例。然而,有一次,在C ++的第一次标准化之前,通过T const*进行的删除被认为是如此不自然(并且在构造和破坏期间对象的变化的持续性得到了很好的理解){{1}这样的指针无效。

对于第一个问题,常见的解决方案是使用delete p;标题中的std::move

<utility>

对于第二个问题,一个解决方案是为每个ConstPtrCustomDelete c1( move( a ) ); ConstPtrCustomDelete c2( move( b ) ); 配备一个自定义删除工具,例如unique_ptr的实例。

另一种解决方案是在您需要自定义删除器的情况下使用更接受的删除器类型,即 1 处理std::default_delete<T const>的那个 - ness:

const

您的示例代码template< class Type > class Deleter { private: function<void(Type const*)> d_; public: using Mutable_type = typename remove_const<Type>::type; void operator()( Type const* p ) const { d_( p ); } Deleter(): d_( default_delete<Type const>() ) {} Deleter( default_delete<Mutable_type> const& ) : d_( default_delete<Type const>() ) {} template< class D > Deleter( D const& d ) : d_( [d]( Type const* p ) { d( const_cast<Mutable_type*>( p ) ); } ) {} }; 以及此类自定义删除工具

move

<子> 1 对于罕见的情况,typedef std::unique_ptr<MyObject> nonConstPtrDefaultDelete; typedef std::unique_ptr<MyObject, Deleter<MyObject>> nonConstPtrCustomDelete; typedef std::unique_ptr<const MyObject, Deleter<MyObject const>> ConstPtrCustomDelete; nonConstPtrDefaultDelete a; nonConstPtrCustomDelete b; ConstPtrCustomDelete c1( move( a ) ); ConstPtrCustomDelete c2( move( b ) ); 存在潜在问题,即当对象最初为const_cast且删除函数const调用某些非正式UB时 - 析构函数之前的d成员函数。这可以通过使用模板参数使此转换支持成为明确的选择来解决。即客户端代码明确保证普通的良好功能。

答案 2 :(得分:3)

这可以解决您的问题:

template<class T>
using smarter_default_delete = std::default_delete<const T>;

template<class T, class D=smarter_default_delete<T>>
using my_unique_ptr = std::unique_ptr<T, D>;

typedef my_unique_ptr<MyObject> nonConstPtrDefaultDelete;

基本上,default_delete不接受T const*。以上修正了。

您还可以跳过其他两种类型的箍,以强制转换工作。但上面看起来更容易。

答案 3 :(得分:1)

为什么在这里使用std::function

您是否真的需要nonConstPtrCustomDelete的不同实例来拥有共享相同签名的不同删除者,或者所有实例都使用相同的删除者?

即。你有没有这样做过:

void foo_deleter(MyObject*);
void bar_deleter(MyObject*);

nonConstPtrCustomDelete ptr1{ foo(), foo_deleter };
nonConstPtrCustomDelete ptr1{ bar(), bar_deleter };

或仅此:

nonConstPtrCustomDelete ptr1{ foo(), foo_deleter };
nonConstPtrCustomDelete ptr1{ bar(), foo_deleter };

如果只有后者那么使用std::function是没有意义的,而且效率低下。如果始终使用相同的删除器,则不需要多态类型擦除的函数对象。

相反,您应该编写自定义删除器类型:

struct non_const_deleter {
  void operator()(MyObject*) const;
};

然后你的typedef可以是:

typedef std::unique_ptr<MyObject, non_const_deleter>
   nonConstPtrCustomDelete;

它也更容易使用,因为删除器可以默认构造,因此您不需要明确地提供它:

nonConstPtrCustomDelete ptr1{ foo() };

完全等同于:

nonConstPtrCustomDelete ptr1{ foo(), non_const_deleter{} };

现在,您可以添加non_const_deleter可以转换为的第二个删除类型:

struct const_deleter {
  const_deleter() = default;
  const_deleter(const non_const_deleter&) { }
  void operator()(const MyObject*) const;
};

或者只是让一种类型处理两种情况:

struct const_and_non_const_deleter {
  void operator()(MyObject*) const;
  void operator()(const MyObject*) const;
};

然后两种类型都可以使用相同的删除器:

typedef std::unique_ptr<MyObject, const_and_non_const_deleter>
   nonConstPtrCustomDelete;
typedef std::unique_ptr<const MyObject, const_and_non_const_deleter>
   constPtrCustomDelete;

并且在将nonConstPtr转换为constPtr时不会有问题。