C++"智能指针"模板,可自动转换为裸指针但无法显式删除

zwo*_*wol 5 c++ templates smart-pointers

我正在使用一个非常大的遗留C++代码库,它仍然是无名的.作为遗留代码库,它会在整个地方传递原始指针.但我们正在逐步尝试对其进行现代化,因此也有一些智能指针模板.这些智能指针(与Boost的scoped_ptr不同)具有对原始指针的隐式转换,因此您可以将其中一个传递给一个采用原始指针而无需编写的例程.get().一个很大的缺点是你也可能在一个delete声明中意外地使用一个,然后你有一个双重免费的bug,这可能是一个真正的痛苦追踪.

有没有办法修改模板,使其仍然具有对原始指针的隐式转换,但如果在delete语句中使用,则会导致编译错误?像这样:

#include <my_scoped_ptr>

struct A {};
extern void f(A*);

struct B
{
    scoped_ptr<A> a;

    B();
    ~B();
};

B::B()
    : a(new A)
{
    f(a); // this should compile
}

B::~B()
{
    delete a; // this should NOT compile
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*itb 7

标准说

操作数应具有指针类型,或具有指针类型的单个转换函数(12.3.2)的类类型.如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并使用转换后的操作数代替本节其余部分的原始操作数.

您可以(ab)通过声明转换函数的const版本来使用缺少重载决策.在符合标准的编译器上,这足以使它不再适用于delete:

struct A {
  operator int*() { return 0; }
  operator int*() const { return 0; }
};

int main() {
  A a;
  int *p = a; // works
  delete a; // doesn't work
}
Run Code Online (Sandbox Code Playgroud)

结果如下

[js@HOST2 cpp]$ clang++ main1.cpp
main1.cpp:9:3: error: ambiguous conversion of delete expression of type 'A' to a pointer
  delete a; // doesn't work
  ^      ~
main1.cpp:2:3: note: candidate function            
  operator int*() { return 0; }
  ^
main1.cpp:3:3: note: candidate function             
  operator int*() const { return 0; }
  ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

对于在这方面不太符合要求的编译器(EDG/Comeau,GCC),您可以将转换函数设置为模板.delete不期望特定类型,所以这将工作:

template<typename T>
operator T*() { return /* ... */ }
Run Code Online (Sandbox Code Playgroud)

但是,这有一个缺点,你的smartpointer现在可以转换为任何指针类型.虽然实际的转换仍然是一个类型,但是这不会排除前期的转换,而是在很晚之后给出编译时错误.可悲的是,在C++ 03中转换函数似乎不可能使用SFINAE :)另一种方法是从另一个函数返回一个私有嵌套类型指针

struct A {
  operator int*() { return 0; }

private:
  struct nested { };
  operator nested*() { return 0; }
};
Run Code Online (Sandbox Code Playgroud)

现在唯一的问题是转换为void*,在这种情况下,两个转换函数同样可行.@Luther建议的解决方法是从另一个转换函数返回一个函数指针类型,它与GCC和Comeau一起使用,并且void*在通常的转换路径上没有其他问题时解决问题,这与模板解决方案不同

struct A {
  operator int*() { return 0; }

private:
  typedef void fty();
  operator fty*() { return 0; }
};
Run Code Online (Sandbox Code Playgroud)

请注意,这些解决方法仅适用于不符合要求的编译器.