残念ながら、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も防ぎたい.
簡単な方法の 1 つは、変換をさらに明確にすること、つまり名前を付けることです。
つまり、正式な引数にキャリア型を導入するか、追加のダミーの変換名引数を導入するか、パブリック変換コンストラクターをパブリック ファクトリ関数に置き換えるかのいずれかです。ファクトリ関数は最も単純で、2012 年の時点でコストがかかりませんが、メンテナンスにコストがかかります。その機能を提供するには、派生クラスで再実装する必要があります。ここで言及されている他の 2 つの解決策のうち、追加の仮名引数は、実装が最も簡単で、コードも最小です。
#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.
}
それでは、代わりに、直接コピーを禁止したいすべての型に対してプライベート コンストラクターと代入演算子を宣言することの何が問題なのですか?
まあ、脆弱であることに加えて、O( n 2 ) 宣言に相当するn型の場合です。したがって、一般的には良い考えではありません。しかし、私がこれを書いているとき、OPはそのアプローチを「解決策」として選択しました。ただ注意してください:一般的に、これは良い考えではありません。