不幸的是,OP 提供了无法编译的代码。
这是以最自然的方式更正的原始代码,以便它编译:
class Type1Str : public string
{
public:
Type1Str() {}
Type1Str(const string & str) : string(str) {}
};
class Type2Str : public string
{
public:
Type2Str() {}
Type2Str(const string & str) : string(str) {}
};
假设上面的意思是,防止交叉分配所需要的就是制作转换构造函数explicit
。
#include <string>
using std::string;
class Type1Str : public string
{
public:
Type1Str() {}
explicit Type1Str(const string & str) : string(str) {}
};
class Type2Str : public string
{
public:
Type2Str() {}
explicit Type2Str(const string & str) : string(str) {}
};
int main()
{
Type1Str t1;
Type2Str t2;
t1 = t2; // A. !Invalid, involves an implicit conversion.
Type2Str t2_2(t1); // B. Still allowed, here the conversion is explicit.
}
在标记为 B 的行中,调用的转换不是实际参数的隐式转换,而是Type2Str
的显式转换构造函数。实际参数t1
直接匹配该构造函数的形式参数,因为t2
是std::string
. OP 也想阻止 B 行。
一种简单的方法是使转换更加明确,即命名。
即,为形式参数引入载体类型,或引入额外的虚拟转换名称参数,或将公共转换构造函数替换为公共工厂函数。工厂函数是最简单的,并且截至 2012 年免费重新效率,但它有维护成本:派生类必须重新实现它才能提供该功能。在这里提到的其他两个解决方案中,额外的虚拟名称参数是最容易实现且代码最少的:
#include <string>
using std::string;
namespace from { enum StdString { stdString }; };
class Type1Str : public string
{
public:
Type1Str() {}
Type1Str( from::StdString, const string & str) : string(str) {}
};
class Type2Str : public string
{
public:
Type2Str() {}
Type2Str( from::StdString, const string & str) : string(str) {}
};
int main()
{
Type1Str t1;
Type2Str t2;
Type2Str t3( from::stdString, t1 ); // OK.
t1 = t2; // A. !Invalid, no conversion possible.
Type2Str t2_2(t1); // B. !Invalid, no conversion possible.
}
那么,仅仅为所有想要禁止直接复制的类型声明私有构造函数和赋值运算符有什么问题呢?
好吧,除了脆弱之外,对于n类型的声明,它相当于 O( n 2 )。所以这通常不是一个好主意。然而,在我写这篇文章时,OP 选择了一个采用这种方法的答案作为“解决方案”。请注意:总的来说,这真的不是一个好主意。