我喜欢并且经常使用 C++ 零规则,但现在有一个用例,我发现它很难使用。
我有 2 个库,都是单独维护的,甚至用不同的编译器编译成不同的 DLL。一个库使用标头并调用另一库的函数。两者都是用 C++ 编写的。
现在,假设我需要添加一个接收常量数据(例如字符串数组)的函数。在普通的 C++ 函数中我会这样做:
struct Obj {
std::vector<std::string> strings;
};
void func(const Obj& obj);
但由于缺乏 C++ ABI 保证,我不能这样做。
我考虑过使用
std::unique_ptr
,但是 它的 ABI 也无法保证。
所以我可以做这样的事情:
class Obj {
public:
Obj(std::initializer_list<std::string_view> strings) {
// Allocate StringsWithAbi and copy strings
}
// Rule of Five goes here :(
private:
struct SymbolString {
char* string;
size_t length;
};
SymbolString* StringsWithAbi{};
};
void func(const std::vector<std::string> strings);
这需要诉诸五法则并仔细实现复制/移动构造函数和其余的,这很容易出错并且只是令人不快的样板。
我能做一些更简单的事情吗?请注意,我希望也能够在 C++ 中使用
Obj
,至少能够在构造、移动和复制等基本操作中使用。我不想创建它的另一个副本只是为了调用 func
。
是否有像
unique_ptr
这样具有稳定 ABI 的低级实用程序?还有更好的想法吗?
我不知道这是否是作弊,但是......为什么不使用实用程序类并在适当的情况下重用它呢?据我所知,否则你所要求的就是不可能的。
(未测试)
template <class T>
class HeapObject;
template<> class HeapObject<void> {
HeapObject(void * storage) : storage_(storage) {}
HeapObject(HeapObject && rhs) : storage_(rhs.storage_) {
rhs.storage_ = nullptr;
}
HeapObject & operator=(HeapObject && rhs) {
storage_ = rhs.storage_;
rhs.storage_ = nullptr;
return *this;
}
HeapObject & operator=(HeapObject const &) = delete;
protected:
void * storage_;
};
template <class T>
struct HeapObject : HeapObject<void> {
public:
T * operator->() {
return reinterpret_cast<T*>(storage_);
}
T const * operator->() const {
return reinterpret_cast<T*>(storage_);
}
T const * operator*() const {
return reinterpret_cast<T const *>(storage_);
}
T * operator*() const {
return reinterpret_cast<T const *>(storage_);
}
~HeapObject() {
delete reinterpret_cast<T*>(storage_);
}
};
struct Obj {
// This or pimpl
HeapObject<std::vector<std::string>> vec_{new std::vector<std::string>{}};
};
void func(const Obj& obj);