所以我有一个提供字符串类型的现有库。
它隐式转换为C样式字符串,如下所示:
struct TypeIDoNotOwn {
TypeIDoNotOwn() {}
TypeIDoNotOwn(TypeIDoNotOwn const&) {}
TypeIDoNotOwn(char const*) {}
TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;}
TypeIDoNotOwn& operator=(char const*) {return *this;}
operator char const*() const {return nullptr;}
};
它有其他方法,但我不认为它们很重要。这些方法有实体,但我的问题并不涉及它们,所以我把它们剔除了。
我想要做的是创建一个可以与上述类型相对可互换使用的新类型,以及"raw string constants"
。我希望能够获取TypeIDoNotOwn
的实例,并将其替换为TypeIDoOwn
,并编译代码。
作为一个例子,这组操作:
void test( TypeIDoNotOwn const& x ) {}
int main() {
TypeIOwn a = TypeIDoNotOwn();
TypeIDoNotOwn b;
a = b;
b = a;
TypeIOwn c = "hello";
TypeIDoNotOwn d = c;
a = "world";
d = "world";
char const* e = a;
std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() );
std::pair<TypeIOwn, TypeIOwn> g = std::make_pair( TypeIDoNotOwn(), TypeIDoNotOwn() );
test(a);
}
如果我将TypeIOwn
替换为上面的TypeIDoNotOwn
,则会进行编译。如何在不使用修改TypeIOwn
的情况下使用TypeIDoNotOwn
进行编译?并且没有必须在声明点引入除了类型更改之外的任何演员表或更改?
我的第一次尝试看起来有点像这样:
struct TypeIOwn {
TypeIOwn() {}
operator char const*() const {return nullptr;}
operator TypeIDoNotOwn() const {return {};}
TypeIOwn( TypeIOwn const& ) {}
TypeIOwn( char const* ) {}
TypeIOwn( TypeIDoNotOwn const& ) {}
TypeIOwn& operator=( char const* ) {return *this;}
TypeIOwn& operator=( TypeIOwn const& ) {return *this;}
TypeIOwn& operator=( TypeIDoNotOwn const& ) {return *this;}
};
但是我得到了一系列模糊的重载:
main.cpp:31:4: error: use of overloaded operator '=' is ambiguous (with operand types 'TypeIDoNotOwn' and 'TypeIOwn') b = a; ~ ^ ~ main.cpp:9:17: note: candidate function TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;} ^ main.cpp:10:17: note: candidate function TypeIDoNotOwn& operator=(char const*) {return *this;}
和
/usr/include/c++/v1/utility:315:15: error: call to constructor of 'TypeIDoNotOwn' is ambiguous : first(_VSTD::forward<_U1>(__p.first)), ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:40:51: note: in instantiation of function template specialization 'std::__1::pair<TypeIDoNotOwn, TypeIDoNotOwn>::pair<TypeIOwn, TypeIOwn>' requested here std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() ); ^ main.cpp:7:7: note: candidate constructor TypeIDoNotOwn(TypeIDoNotOwn const&) {} ^ main.cpp:8:7: note: candidate constructor TypeIDoNotOwn(char const*) {} ^
在我的“真实”代码中,我有其他运算符,例如+=
和==
,它们有类似的问题。
真正问题的范围很大;数以百万计的代码行,我想在数千个位置换掉TypeIDo的TypeIDoNotOwn,而不是数百个其他位置。在数千个地点,他们以导致转换模糊的方式进行互动。
我已经解决了一个函数问题,即函数在100个点上发生TypeIDoNotOwn&
,它通过一个宏包装它来创建一个临时对象,从TypeIDoNotOwn
创建一个TypeIOwn
返回对该引用的引用,然后在销毁临时对象时将其复制回TypeIOwn
。我希望避免执行类似的扫描来处理==
,+=
,=
,复制构造和类似情况。
如果我尝试删除operator TypeIDoNotOwn
以消除这种歧义,那么转换需要发生的其他情况就无法正常工作(因为它需要2个用户定义的结构才能从TypeIOwn
转到TypeIDoNotOwn
),然后需要进行显式转换(在100多个或1000个位置)
如果我可以让一个转换看起来比另一个更糟糕,它会起作用。如果做不到这一点,我可以通过重载一个具有完全匹配的免费operator=
运算符(和其他情况类似)来修复非TypeIDoNotOwn == TypeIOwn
和复制构造案例,但这不会让我构建,函数调用和赋值。
答案 0 :(得分:2)
通常需要注意的是这是C ++,并且必然会有一些聪明的解决方法......不。
让我们来看看你的用例。您希望复制初始化和复制分配都能正常工作:
TypeIOwn a = ...;
TypeIDoNotOwn b = a; // (*)
TypeIDoNotOwn c;
c = a; // (*)
这需要:
operator TypeIDoNotOwn();
如果您刚刚提供了operator const char*()
,那么分配将起作用,但复制初始化将失败。如果你提供了两者,那就不明确了,因为没有办法强迫一个转换优先于另一个转换(强制转换排序的唯一真正方法是创建类型层次结构,但你不能继承const char*
所以你不能真的强迫它工作)。
一旦我们开始只使用一个转换函数,从示例列表中无效的唯一代码是:
const char* e = a; // error: no viable conversion
此时,您必须添加成员函数:
const char* e = a.c_str();
两个pair
构造都适用于一个转换函数。但只是通过消除过程,我们不能同时拥有两者。
答案 1 :(得分:1)
没有灵丹妙药,但你可以通过声明从TypeIOwn到TypeIDoNotOwn的转换为显式来获得一些改进。
explicit operator TypeIDoNotOwn() const { return{}; }
这意味着您必须在发生这种情况的每个位置进行更改,但它确实解决了问题&#34; const char *&#34;对作业同样有效。是值得的权衡吗?你必须做出决定。
然而,为了逐步改变代码库,我在使用不同策略的类似情况下运气不错。我只是设置一个#define标志并使用完全一个或另一个进行编译,我可以继续使用TypeIDoNotOwn进行正常编码,同时在使所有内容与TypeIDoOwn一起工作方面取得进展。
#ifdef SOME_FLAG
struct TypeIOwn {...};
typedef TypeIOwn TypeIDoNotOwn;
#else
struct TypeIDoNotOwn {...};
#endif
您必须为每次更新测试两者,直到最终完成。
因为你说这是一个字符串类,所以也考虑转向std :: string的选项,所以你的TypeIOwn成为std :: string的瘦包装器,不再提供对const char *的隐式转换。相反,提供data()。您不再需要来自TypeIOwn的模糊转换 - &gt; (const char * | TypeIDoNotOwn) - &gt; TypeIDoNotOwn,因为像std :: string一样,你不再允许隐式转换为const char *,并且当你完全抛弃两个字符串类并使用std :: string时,你付出的任何工作都会得到回报。 / p>