C++でexplicit
キーワードはどういう意味ですか?
コンパイラーは、パラメーターを関数に解決するために暗黙的な変換を1回行うことができます。これが意味することは、コンパイラは 単一のパラメータ とともに呼び出し可能なコンストラクタを使用して、パラメータの正しい型を取得するためにある型から別の型に変換できることです。
これは暗黙的な変換に使用できるコンストラクタを持つクラスの例です。
class Foo
{
public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo)
{
}
int GetFoo () { return m_foo; }
private:
int m_foo;
};
これはFoo
オブジェクトを取る簡単な関数です。
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
そしてここでDoBar
関数が呼び出されます。
int main ()
{
DoBar (42);
}
引数はFoo
オブジェクトではなくint
です。ただし、Foo
を取るint
のコンストラクタが存在するため、このコンストラクタを使用してパラメータを正しい型に変換できます。
コンパイラーは各パラメーターにつき1回これを行うことができます。
コンストラクタの先頭にexplicit
キーワードを付けると、コンパイラは暗黙の変換にそのコンストラクタを使用できなくなります。上記のクラスに追加すると、関数呼び出しDoBar (42)
でコンパイラエラーが発生します。 DoBar (Foo (42))
で明示的に変換を呼び出す必要があります
あなたがこれをしたいと思うかもしれない理由はバグを隠すことができる偶然の建設を避けるためです。考案された例:
MyString(int size)
クラスがあります。あなたは関数print(const MyString&)
を持っています、そしてあなたはprint(3)
を呼び出します(あなたが 実際に がprint("3")
を呼び出すことを意図していた時)。あなたはそれが "3"を印刷することを期待していますが、代わりに長さ3の空の文字列を印刷します。String
というクラスがあるとします。
class String {
public:
String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
さて、あなたが試した場合:
String mystring = 'x';
文字'x'
は暗黙的にint
に変換されてからString(int)
コンストラクタが呼び出されます。しかし、これはユーザーが意図したものではありません。そのため、このような状態を防ぐために、コンストラクタをexplicit
として定義します。
class String {
public:
explicit String (int n); //allocate n bytes
String(const char *p); // initialize sobject with string p
};
C++では、必須パラメーターが1つだけのコンストラクターは、暗黙的な変換関数と見なされます。パラメータの型をクラスの型に変換します。これが良いことであるかどうかは、コンストラクタのセマンティクスに依存します。
たとえば、コンストラクタString(const char* s)
を持つ文字列クラスがある場合、それはおそらくあなたが望むものです。 String
を期待する関数にconst char*
を渡すことができます、そしてコンパイラはあなたのために一時的なString
オブジェクトを自動的に構築します。
一方、コンストラクタBuffer(int size)
がバッファのサイズをバイト単位で取るバッファクラスがある場合、おそらくコンパイラにint
sをBuffer
sに静かに変換させたくないでしょう。それを防ぐために、コンストラクタをexplicit
キーワードで宣言します。
class Buffer { explicit Buffer(int size); ... }
そのように、
void useBuffer(Buffer& buf);
useBuffer(4);
コンパイル時エラーになります。一時的なBuffer
オブジェクトを渡したい場合は、明示的に渡す必要があります。
useBuffer(Buffer(4));
まとめると、単一パラメータのコンストラクタがパラメータをクラスのオブジェクトに変換する場合、おそらくexplicit
キーワードを使用したくないでしょう。しかし、たった1つのパラメータをとるようなコンストラクタがある場合は、コンパイラが予期しない変換をしても驚かないように、それをexplicit
として宣言する必要があります。
この答えは他の答えでカバーされていないので明白なコンストラクタの有無にかかわらずオブジェクト作成についてである。
明示的なコンストラクタのない次のクラスを考えてください。
class Foo
{
public:
Foo(int x) : m_x(x)
{
}
private:
int m_x;
};
クラスFooのオブジェクトは2つの方法で作成できます。
Foo bar1(10);
Foo bar2 = 20;
実装によっては、クラスFooをインスタンス化する2番目の方法は、混乱を招くか、プログラマが意図したものではない可能性があります。コンストラクタの前にexplicit
キーワードを付けると、Foo bar2 = 20;
にコンパイラエラーが発生します。
実装によって明示的に禁止されていない限り、単一引数のコンストラクタをexplicit
として宣言することが通常はグッドプラクティス)です。
また、コンストラクタは
両方とも単一引数のコンストラクタとして使用できます。ですから、これらもexplicit
にしたいと思うかもしれません。
意図的にnotにして単一引数のコンストラクタを明示的にしたい場合の例は、ファンクタを作成している場合です( this で宣言された 'add_x'構造体を見てくださいそのような場合、add_x add30 = 30;
としてオブジェクトを作成することはおそらく意味があります。
Here は明示的なコンストラクタについての良い記事です。
explicit
キーワードは、変換コンストラクタを非変換コンストラクタにします。その結果、コードのエラーが少なくなります。
explicit
はどちらかを伴いますC++ [class.conv.ctor]
1)関数指定子を明示的に指定せずに宣言されたコンストラクタは、そのパラメータの型からそのクラスの型への変換を指定します。このようなコンストラクタは変換コンストラクタと呼ばれます。
2)明示的コンストラクタは、非明示的コンストラクタとまったく同じようにオブジェクトを構成しますが、直接初期化構文(8.5)またはキャスト(5.2.9、5.4)が明示的に使用されている場合にのみそうします。デフォルトのコンストラクタは明示的なコンストラクタにすることができます。そのようなコンストラクタは、default-initializationまたはvalueinitialization(8.5)を実行するために使用されます。
C++ [class.conv.fct]
2)変換関数は明示的(7.1.2)でもかまいません。その場合、直接初期化のためのユーザー定義の変換としてのみ考慮されます(8.5)。それ以外の場合、ユーザー定義の変換は、割り当ておよび初期化での使用に制限されません。
明示的な変換関数とコンストラクタは、明示的な変換(直接初期化または明示的なキャスト操作)にのみ使用できます。一方、非明示的なコンストラクタと変換関数は、暗黙的および明示的な変換に使用できます。
/*
explicit conversion implicit conversion
explicit constructor yes no
constructor yes yes
explicit conversion function yes no
conversion function yes yes
*/
X, Y, Z
と関数foo, bar, baz
を使った例:explicit
変換とnon -explicit
変換の違いを見るために、構造と関数の小さなセットアップを見てみましょう。
struct Z { };
struct X {
explicit X(int a); // X can be constructed from int explicitly
explicit operator Z (); // X can be converted to Z explicitly
};
struct Y{
Y(int a); // int can be implicitly converted to Y
operator Z (); // Y can be implicitly converted to Z
};
void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }
関数の引数の変換
foo(2); // error: no implicit conversion int to X possible
foo(X(2)); // OK: direct initialization: explicit conversion
foo(static_cast<X>(2)); // OK: explicit conversion
bar(2); // OK: implicit conversion via Y(int)
bar(Y(2)); // OK: direct initialization
bar(static_cast<Y>(2)); // OK: explicit conversion
オブジェクトの初期化
X x2 = 2; // error: no implicit conversion int to X possible
X x3(2); // OK: direct initialization
X x4 = X(2); // OK: direct initialization
X x5 = static_cast<X>(2); // OK: explicit conversion
Y y2 = 2; // OK: implicit conversion via Y(int)
Y y3(2); // OK: direct initialization
Y y4 = Y(2); // OK: direct initialization
Y y5 = static_cast<Y>(2); // OK: explicit conversion
X x1{ 0 };
Y y1{ 0 };
関数の引数の変換
baz(x1); // error: X not implicitly convertible to Z
baz(Z(x1)); // OK: explicit initialization
baz(static_cast<Z>(x1)); // OK: explicit conversion
baz(y1); // OK: implicit conversion via Y::operator Z()
baz(Z(y1)); // OK: direct initialization
baz(static_cast<Z>(y1)); // OK: explicit conversion
オブジェクトの初期化
Z z1 = x1; // error: X not implicitly convertible to Z
Z z2(x1); // OK: explicit initialization
Z z3 = Z(x1); // OK: explicit initialization
Z z4 = static_cast<Z>(x1); // OK: explicit conversion
Z z1 = y1; // OK: implicit conversion via Y::operator Z()
Z z2(y1); // OK: direct initialization
Z z3 = Z(y1); // OK: direct initialization
Z z4 = static_cast<Z>(y1); // OK: explicit conversion
explicit
変換関数やコンストラクタを使うのですか?変換コンストラクタと非明示的な変換関数はあいまいさをもたらすかもしれません。
V
に変換可能な構造体int
、U
から暗黙的に構築可能な構造体V
、およびf
およびU
に対してそれぞれオーバーロードされた関数bool
を考えてください。
struct V {
operator bool() const { return true; }
};
struct U { U(V) { } };
void f(U) { }
void f(bool) { }
f
型のオブジェクトを渡す場合、V
の呼び出しはあいまいです。
V x;
f(x); // error: call of overloaded 'f(V&)' is ambiguous
コンパイラは、U
のコンストラクタやV
オブジェクトをf
に渡すための型に変換する変換関数を使用する必要があるかどうかを知りません。
U
のコンストラクタまたはV
の変換関数がexplicit
である場合、非明示的な変換のみが考慮されるため、あいまいさはありません。両方とも明示的な場合、f
型のオブジェクトを使用したV
の呼び出しは、明示的な変換操作またはキャスト操作を使用して行う必要があります。
変換コンストラクタと非明示的な変換関数が予期しない動作を引き起こす可能性があります。
あるベクトルを出力する関数を考えてみましょう:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
ベクトルのサイズコンストラクタが明示的ではない場合は、次のように関数を呼び出すことが可能です。
print_intvector(3);
そのような電話から何を期待するでしょうか? 3
を含む1行または0
を含む3行? (2番目のものは何が起こるかです。)
Bjarne Stroustrupはそれを( "The C++ Programming Language"、4th Ed。、35.2.1、pp。1011)、なぜstd::duration
が普通の数から暗黙のうちに構築することができないのかという疑問に置きます。
自分の意味を知っている場合は、それについて明確にしてください。
explicit
-キーワードは 明示的に と呼ばれるようにコンストラクタを強制するために使用することができます。
class C{
public:
explicit C(void) = default;
};
int main(void){
C c();
return 0;
}
コンストラクタC(void)
の前にあるexplicit
-キーワードは、このコンストラクタへの明示的な呼び出しだけが許可されることをコンパイラに伝えます。
explicit
-キーワードは、ユーザー定義型キャスト演算子でも使用できます。
class C{
public:
explicit inline operator bool(void) const{
return true;
}
};
int main(void){
C c;
bool b = static_cast<bool>(c);
return 0;
}
ここでは、explicit
- keywordは明示的なキャストのみが有効であることを強制しているため、この場合はbool b = c;
は無効なキャストになります。これらのexplicit
-キーワードのような状況では、暗黙の意図しないキャストを避けるためにプログラマーを助けることができます。この用法は C++ 11 で標準化されています。
明示的変換コンストラクタ(C++のみ)
明示的関数指定子は、不要な暗黙的な型変換を制御します。クラス宣言内のコンストラクタの宣言でのみ使用できます。たとえば、デフォルトのコンストラクタを除いて、次のクラスのコンストラクタは変換コンストラクタです。
class A
{
public:
A();
A(int);
A(const char*, int = 0);
};
次の宣言は有効です。
A c = 1;
A d = "Venditti";
最初の宣言はA c = A( 1 );
と同じです。
クラスのコンストラクタをexplicit
として宣言した場合、それ以前の宣言は違法になります。
たとえば、クラスを次のように宣言したとします。
class A
{
public:
explicit A();
explicit A(int);
explicit A(const char*, int = 0);
};
クラスタイプの値と一致する値のみを割り当てることができます。
たとえば、次の文は有効です。
A a1;
A a2 = A(1);
A a3(1);
A a4 = A("Venditti");
A* p = new A(1);
A a5 = (A)1;
A a6 = static_cast<A>(1);
これはすでに議論されています( 明示的コンストラクタとは )。しかし、私はそれがここに見られる詳細な説明を欠いていると言わなければなりません。
その上、すでに述べたように、引数を1つのコンストラクタ(arg2、arg3、...のデフォルト値を持つものも含む)にすることは、常に良いコーディング方法です。 C++といつものように:そうでなければ - あなたはそうしたかったでしょう….
あなたが本当にそれを実装する必要がない限り、クラスのためのもう一つの良い習慣はコピー構築と代入を非公開にすることです(別名それを無効にする)。これにより、C++がデフォルトで作成するメソッドを使用するときに、最終的にポインタのコピーが作成されるのを防ぎます。これを行う他の方法はboost :: noncopyableから派生することである。
Cppの参照は常に役に立ちます!明示的な指定子についての詳細は ここ で見つけることができます。 暗黙的な変換 および コピー初期化 も参照する必要があります。
ちょっと見に来ます
明示的な指定子は、コンストラクタまたは変換関数(C++ 11以降)が暗黙的な変換またはコピー初期化を許可しないことを指定します。
次のような例
struct A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
operator bool() const { return true; }
};
struct B
{
explicit B(int) { }
explicit B(int, int) { }
explicit operator bool() const { return true; }
};
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
if (a1) cout << "true" << endl; // OK: A::operator bool()
bool na1 = a1; // OK: copy-initialization selects A::operator bool()
bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
if (b5) cout << "true" << endl; // OK: B::operator bool()
// bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
}
コンストラクタは暗黙的な変換を追加します。この暗黙の型変換を抑制するためには、パラメータexplicitを使ってコンストラクタを宣言する必要があります。
C++ 11では、そのようなキーワードで "operator type()"を指定することもできます http://en.cppreference.com/w/cpp/language/explicit このような仕様では、次のような演算子を使用できます。明示的な変換、およびオブジェクトの直接初期化.
P.S BYコンストラクターおよび型変換演算子によってBY USERで定義された変換を使用する場合は、暗黙的な変換を1レベルだけ使用できます。しかし、あなたは他の言語変換とこの変換を組み合わせることができます