用于在 auto_ptr 和 unique_ptr 之间切换的宏

macro for switching between auto_ptr and unique_ptr

在一个仍然使用 C++11 之前版本的项目中,我想通过使用 C++11 编译器编译并修复错误来为切换准备源代码。他们包括

现在我想切换回 C++ 之前的编译器并编写一个可以切换回更改的宏,这样,当最后的编译器切换时间到了时,我只需删除宏。我试过了

#ifndef HAVE_CXX11
#define nullptr NULL
namespace std {
#define unique_ptr<exvector> auto_ptr<exvector>
}
#endif

(使用 exvector 一个与智能指针一起使用的示例类型)这个和类似的尝试都不起作用,因为宏不能更改模板类型。我也用了 typedef 没有更好的结果。

这是否可能,如果可能,如何实现?

对于 nullptrunique_ptr,这可能有效:

#ifndef HAVE_CXX11
  #define nullptr NULL
  #define unique_ptr auto_ptr
#endif

但我不知道你打算如何应对 unique_ptrauto_ptr 的不同语义。

如果您愿意暂时忍受一些未定义的行为(不太可能导致实际问题的那种),您也可以提供自己的 std::move:

namespace std {

template <class T>
T& move(T &x) { return x; }

}

它是 UB,因为您不能向 namespace std 添加任何内容。但如果只是临时措施,应该是安全的(pre-11的编译器不太可能有std::move这个名字)。

怎么样

#ifdef NOT_HAVING_CPP_11 
namespace std{
template<typename T>
struct unique_ptr : public auto_ptr<T>{}; }
#endif

这样,在将旧代码中的所有 auto_ptr 实例替换为 unique_ptr 后,您可以删除 NOT_HAVING_CPP_11 宏,并且在现代编译器上编译时不会发出警告.

#if no_Cxx11
  template<class T>
  struct up:auto_ptr<T> {
    up(T* t):auto_ptr(t) {}
    up(auto_ptr& o):auto_ptr(o) {}
  };
  T& my_move(T& t){return t;}
  struct my_nullptr_t {
    template<class T>
    operator T*()const{return NULL;}
    // template operator T* isn't always looked for:
    operator void*()const{return NULL;}
  };
  static const my_nullptr_t nullptr;
#else
  template<class T>
  using up=std::unique_ptr<T>;
  template<class T>
  decltype(std::move(std::declval<T>()))
  my_move(T&& t) {
    return std::move(std::forward<T>(t));
  }
  using my_nullptr_t = std::nullptr_t;
#endif

这需要在代码库中将 std::unique_ptr 替换为 up,将 std::nullptr_t 替换为 my_nullptr_t,将 std::move 替换为 my_move。在C++11中,它们都是C++11。在 C++03 中,它们被模拟。

你甚至可以更进一步,把my_move教给return一个lvalue_up,让up拒绝从up中复制C++03 模式(手动移动语义)。当您在 C++03 中从容器移动时,您甚至可以在容器上设置自动交换。

我会引入非常明确的宏,例如

//Defines to overcome pre C++11 limitations regarding 
//std::auto_ptr and absence of 'good' move (i.e. std::move) semantics.
//Strictly post C++11 code should use std::unique_ptr and std::move explicitly.
//All other code should use the macros.
//If pre-C++11 ceases to be a target the macros may be replaced...
#ifdef HAVE_CXX11 //Or whatever...

#define UNIQUE_PTR_TYPE std::unique_ptr
#define MOVE_UNIQUE_PTR(PTR) std::move(PTR)

#else

#define UNIQUE_PTR_TYPE std::auto_ptr
#define MOVE_UNIQUE_PTR(PTR) (PTR)

#endif

为什么?因为即使是不经意的 reader 也会看到一些替换正在进行。

是的,代码看起来很丑但是很安全"not going to blow anyone's fingers off ugly"。我们是工程师而不是诗人,这就是我们的美!

但是我不得不说我同意发帖者认为你应该对代码进行分支的观点。这不是唯一的不兼容性,你的代码会变得越来越臃肿,你可能会发现你正在做更多的工作(并引入更多的错误)试图使单个分支多目标而不是分支。

宏是美妙的东西,但任何形式:

#define <common symbol> <something else>

需要 100% 保证良性 "you don't need to know it was substituted" 才能被宽恕。

我只是不认为:

#define unique_ptr auto_ptr

或任何其他使此替换不可见的东西都完全通过了该测试。 unique_ptrauto_ptr 不一样, auto_ptr 被弃用的全部原因是你需要小心使用它。

对于 lulz(回到我关于隐形替代品的观点)尝试:

#define if(A) if(((A)&&rand()>128)||rand()>(RAND_MAX-128))

这应该能让这些混蛋们忙一下午…… 最好的一点是你没有播种 srand() 失败将是可重复的!