switch
ステートメントとif
ステートメントの変換演算子の動作が異なるのはなぜですか?
struct WrapperA
{
explicit operator bool() { return false; }
};
struct WrapperB
{
explicit operator int() { return 0; }
};
int main()
{
WrapperA wrapper_a;
if (wrapper_a) { /** this line compiles **/ }
WrapperB wrapper_b;
switch (wrapper_b) { /** this line does NOT compile **/ }
}
コンパイルエラーはswitch quantity is not an integer
if
ステートメント内では、bool
として完全に認識されます。 (GCC)
構文はswitch ( condition ) statement
で、
条件-整数型または列挙型、またはクラス型の式contextuallyimplicitly整数型または列挙型に変換できます。または、中括弧または等しいイニシャライザを使用して、そのような型の単一の非配列変数を宣言します。
cppreference。 から取得
つまり、整数または列挙型typeでのみ大文字と小文字を切り替えることができます。コンパイラーが暗黙的にWrapperを整数/列挙型に変換できるようにするには、明示的なキーワードを削除する必要があります。
明示的な指定子は、コンストラクターまたは変換関数(C++ 11以降)が暗黙的な変換を許可しないことを指定します
Wrapperをint型にキャストすることもできます。
@ acraig5075のアドレスを編集してコメント:
どの演算子が明示的で、どの演算子が暗黙的であるかに注意する必要があります。両方が暗黙的である場合、あいまいさがあるため、コードはコンパイルされません。
struct Wrapper
{
operator int() { return 0; }
operator bool() { return true; }
};
source_file.cpp:関数「int main()」内:source_file.cpp:12:14:
エラー:「ラッパー」からのあいまいなデフォルトの型変換
スイッチ(w){
^ source_file.cpp:12:14:注:候補の変換
「Wrapper :: operator int()」と「Wrapper :: operator bool()」を含める
あいまいさを取り除く唯一の方法は、キャストを行うことです。
演算子の1つだけが明示的である場合は、switchステートメントで他の演算子が選択されます。
#include <iostream>
struct Wrapper
{
explicit operator int() { return 0; }
operator bool() { return true; }
};
int main()
{
Wrapper w;
if (w) { /** this line compiles **/std::cout << " if is true " << std::endl; }
switch (w) {
case 0:
std::cout << "case 0" << std::endl;
break;
case 1:
std::cout << "case 1" << std::endl;
break;
}
return 0;
}
出力:
if is true
case 1
w
は暗黙的に1
(true
)に変換され(演算子intが明示的であるため)、ケース1が実行されます。
一方 :
struct Wrapper
{
operator int() { return 0; }
explicit operator bool() { return true; }
};
Ouput:
if is true
case 0
演算子boolは明示的であるため、w
は暗黙的に0
に変換されました。
どちらの場合も、w
がifステートメント内のブール値としてcontextuallyに評価されるため、ifステートメントはtrueです。
this は、switch
ステートメントが受け入れられない理由を説明していると思いますが、if
ステートメントは次のとおりです。
次の5つのコンテキストでは、宣言
bool t(e);
が整形式である場合、ブール型が予期され、暗黙の変換シーケンスが構築されます。つまり、explicit T::operator bool() const;
などの明示的なユーザー定義の変換関数が考慮されます。そのような式eは、文脈的にboolに変換可能であると言われています。
変換演算子explicit
の宣言は、その型への暗黙的な変換を防ぐために存在します。それがその目的です。 switch
は、引数を暗黙的に整数に変換しようとします。したがって、explicit
演算子は呼び出されません。これは予想される動作です。
予期しないのは、explicit
演算子がif
の場合に呼び出されることです。そしてそれによって物語をぶら下げる。
上記のルールを前提として、if
を介して型をテスト可能にする方法は、explicit
以外の変換をbool
に行うことです。問題は... bool
は問題のあるタイプです。暗黙的に整数に変換できます。したがって、暗黙的にbool
に変換可能な型を作成した場合、これは有効なコードです。
_void foo(int);
foo(convertible_type{});
_
しかし、これも無意味なコードです。 _convertible_type
_を暗黙的に整数に変換するつもりはありません。テスト目的でブール値に変換したいだけです。
C++ 11以前では、これを修正する方法は「安全なブールイディオム」でしたが、これは複雑な問題であり、論理的な意味もありませんでした(基本的に、ブール値に変換可能なメンバーポインターへの暗黙的な変換を提供しましたが、整数や通常のポインタではありません)。
したがって、C++ 11では、explicit
変換演算子を追加すると、bool
の例外が発生しました。 explicit operator bool()
がある場合、この型は、言語で定義された一連の場所の中で「コンテキストに応じてbool
に変換」できます。これにより、explicit operator bool()
が「ブール条件でテスト可能」を意味するようになります。
switch
はそのような保護を必要としません。型をint
の目的でswitch
に暗黙的に変換可能にする場合、他の目的でも暗黙的にint
に変換できない理由はありません。
1つの答えは、if
とswitch
は標準が作成された方法であるため、動作が異なるということです。別の答えは、標準がそのように書かれた理由を推測するかもしれません。まあ、私は標準のif
ステートメントが特定の問題(bool
への暗黙の変換 問題があった )に対処するためにそのように動作すると思いますが、私はしたいです別の視点を採用します。
if
ステートメントでは、条件はブール値でなければなりません。だれもがこのようにif
ステートメントについて考えているわけではありません。おそらく、言語に組み込まれたさまざまな便利さのためです。ただし、中核として、if
ステートメントは「これを実行する」または「それを実行する」ことを知っている必要があります。 "はい、もしくは、いいえ"; true
またはfalse
-ブール値。この点で、ステートメントの条件内に何かを入れると、何かをbool
に変換するように明示的に要求されます。
一方、switch
ステートメントは任意の整数型を受け入れます。つまり、他のどのタイプよりも優先される単一のタイプはありません。 switch
の使用は、値を整数型に変換する明示的な要求と見なすことができますが、必ずしもint
に明示的に要求する必要はありません。そのため、特定の変換を明示的に要求する必要がある場合、int
への変換を使用することは適切とは見なされません。
この動作の本当の理由はCにあると思いますが、それを説明する前に、C++の用語でそれを正当化しようと思います。
if
/while
/for
ステートメントは、任意のスカラー(整数、浮動小数点、またはポインター)、またはbool
に変換可能なクラスインスタンスを取得することになっています。値がゼロに等しいかどうかを確認するだけです。この許容度を考えると、explicit
演算子を使用して値をif
ステートメントに合わせるのはコンパイラにとって比較的無害です。
一方、switch
は、整数とenum
sでのみ意味があります。 switch
またはdouble
またはポインターを使用しようとすると、同じエラーが発生します。これにより、離散ケース値の定義が容易になります。 switch
ステートメントは特に整数を参照する必要があるため、暗黙の変換を定義していないクラスインスタンスを使用するのはおそらく間違いです。
歴史的には、その理由はこれがCが行う方法です。
C++はもともとCとの下位互換性を保つことを目的としていました(ただし、C++は成功していません)。 Cは最近までブール型を保持していませんでした。 Cのif
ステートメントは、他の方法がなかったため、寛容でなければなりませんでした。 CはC++のように構造体からスカラーへの型変換を行いませんが、C++のexplicit
メソッドの処理は、if
ステートメントが両方の言語で非常に許容的であるという事実を反映しています。
Cのswitch
ステートメントは、if
とは異なり、離散値を処理する必要があるため、それほど寛容ではありません。
コードには2つの問題があります。まず、変換演算子は、switch
ステートメントを処理するために明示的であってはなりません。次に、switch
条件には整数型が必要であり、int
とbool
はどちらもこのような型であるため、あいまいさが生じます。これらの2つの問題がないようにクラスを変更すると、switch
は期待どおりにコンパイルおよび動作します。
クラスを変更する必要のない別の解決策は、明示的にキャストすることです(static_cast
はint
またはbool
の値を実行します。