その他の演算子
| Operator name |
構文 | オーバーロード可能 | プロトタイプの例 (class T の場合) | |
|---|---|---|---|---|
| クラス定義内 | クラス定義外 | |||
| 関数呼び出し | a(a1, a2)
|
はい | R T::operator()(Arg1 &a1, Arg2 &a2, ...); | N/A |
| コンマ | a, b
|
はい | T2& T::operator,(T2 &b); | T2& operator,(const T &a, T2 &b); |
| 条件演算子 | a ? b : c
|
いいえ | N/A | N/A |
関数呼び出し演算子は、あらゆるオブジェクトに関数セマンティクスを提供します。
条件演算子(口語的には三項条件演算子と呼ばれる)は、最初の式のブール値をチェックし、結果の値に応じて、2番目または3番目の式のいずれかを評価して返します。
目次 |
[編集] 組み込み関数呼び出し演算子
関数呼び出し式は以下の形式をとります。
function (arg1, arg2, arg3,...) |
|||||||||
| 関数 | - | 式関数型または関数ポインタ型 |
arg1, arg2, arg3,... |
- | 任意式(空の場合もある)のリストまたはブレース囲み初期化子リスト(C++11以降)。ただし、曖昧さを避けるため、最上位レベルでコンマ演算子を使用することはできません。 |
非メンバ関数または静的メンバ関数の呼び出しでは、functionは関数を参照する左辺値(この場合、関数からポインタへの変換は抑制される)、または関数ポインタ型の右辺値となります。
functionで指定された関数(またはメンバ)名はオーバーロードされる可能性があり、どのオーバーロードを呼び出すかを決定するためにオーバーロード解決ルールが使用されます。
functionがメンバ関数を指定する場合、それは仮想関数である可能性があり、その場合、実行時に動的ディスパッチを使用して、その関数の最終オーバーライドが呼び出されます。
各関数パラメータは、必要に応じて暗黙的な変換が行われた後、対応する引数で初期化されます。
- 対応する引数がない場合、対応するデフォルト引数が使用され、それも存在しない場合、プログラムは不正となります。
- 呼び出しがメンバ関数に対して行われる場合、現在のオブジェクトへのthisポインタは、関数が期待するthisポインタへの明示的なキャストによって変換されたかのように扱われます。
- 各パラメータの初期化と破棄は、関数呼び出しが出現する完全式のコンテキストで行われます。これは、例えば、パラメータのコンストラクタまたはデストラクタが例外をスローした場合、呼び出された関数の関数tryブロックは考慮されないことを意味します。
関数が可変引数関数である場合、省略記号パラメータに一致するすべての引数にデフォルト引数昇格が適用されます。
パラメータが定義された関数が終了するときに破棄されるか、囲む完全式の最後に破棄されるかは、実装定義です。パラメータは常に、構築とは逆の順序で破棄されます。
関数呼び出し式の戻り値の型は、選択された関数の戻り値の型であり、仮想キーワードを無視した静的結合を使用して決定されます。これは、実際に呼び出されるオーバーライド関数が異なる型を返しても同じです。これにより、オーバーライド関数は、基底関数が返す戻り値の型から派生したクラスへのポインタまたは参照を返すことができます。つまり、C++は共変な戻り値の型をサポートします。functionがデストラクタを指定する場合、戻り値の型はvoidです。
|
クラス型`X`のオブジェクトが関数に渡されるか、関数から返される場合、`X`の各コピーコンストラクタ、ムーブコンストラクタ、およびデストラクタがトリビアルであるか削除されている場合、そして`X`が少なくとも1つの非削除コピーまたはムーブコンストラクタを持つ場合、実装は一時オブジェクトを作成して関数パラメータまたは結果オブジェクトを保持することを許可されます。 一時オブジェクトは、それぞれ関数引数または戻り値から構築され、関数のパラメータまたは戻り値オブジェクトは、非削除トリビアルコンストラクタを使用して一時オブジェクトをコピーするかのように初期化されます(そのコンストラクタがアクセス不能であるか、オブジェクトのコピーまたは移動を実行するためにオーバーロード解決によって選択されない場合でも)。 これにより、std::complex や std::span のような小さなクラス型のオブジェクトをレジスタで関数に渡したり、関数から返したりすることができます。 |
(C++17以降) |
関数呼び出し式の値カテゴリは、関数が左辺値参照または関数への右辺値参照を返す場合は左辺値、関数がオブジェクトへの右辺値参照を返す場合はx値、それ以外の場合は右辺値です。オブジェクト型の右辺値である関数呼び出し式は、完全な型を持つ必要があります。ただし、decltypeのオペランドとして(またはdecltypeのオペランドである組み込みコンマ演算子の右オペランドとして)使用される場合を除きます(C++11以降)。
|
呼び出された関数が正常に終了すると、関数のすべての事後条件アサーションが順に評価されます。実装が結果値を保持するために一時オブジェクトを導入する場合、各事後条件アサーションの評価`E`について
|
(C++26以降) |
関数呼び出し式は、値初期化T()、関数形式キャスト式T(A1)、および一時オブジェクトの直接初期化T(A1, A2, A3, ...)と構文が似ており、ここでTは型の名前です。
#include <cstdio> struct S { int f1(double d) { return printf("%f \n", d); // variable argument function call } int f2() { return f1(7); // member function call, same as this->f1() // integer argument converted to double } }; void f() { puts("function called"); // function call } int main() { f(); // function call S s; s.f2(); // member function call }
出力
function called 7.000000
[編集] 組み込みコンマ演算子
コンマ式は以下の形式をとります
E1 , E2 |
|||||||||
コンマ式E1, E2では、式E1が評価され、その結果は破棄されます(ただし、クラス型の場合、含まれる完全式の終わりまでは破棄されません)。また、その副作用は式E2の評価が始まる前に完了します(ユーザー定義のoperator,は順序付けを保証できないことに注意してください)(C++17まで)。
コンマ式の結果の型、値、および値カテゴリは、2番目のオペランドE2の型、値、および値カテゴリと全く同じです。E2が一時式(C++17以降)の場合、式の結果はその一時式(C++17以降)です。E2がビットフィールドの場合、結果もビットフィールドです。
関数引数リスト(f(a, b, c))や初期化子リストint a[] = {1, 2, 3}など、さまざまなカンマ区切りリスト内のカンマは、カンマ演算子ではありません。そのような文脈でカンマ演算子を使用する必要がある場合は、括弧で囲む必要があります: f(a, (n++, n + b), c)。
|
添字演算子の2番目(右)引数として、括弧なしのコンマ式を使用することは非推奨です。 例えば、a[b, c] は非推奨であり、a[(b, c)] は非推奨ではありません。 |
(C++20以降) (C++23まで) |
|
括弧なしのコンマ式は、添字演算子の2番目(右)引数にはなれません。例えば、a[b, c] は不正な形式であるか、またはa.operator[](b, c) と同等です。 コンマ式を添字として使用する場合は括弧が必要です。例:a[(b, c)]。 |
(C++23から) |
#include <iostream> int main() { // comma is often used to execute more than one expression // where the language grammar allows only one expression: // * in the third component of the for loop for (int i = 0, j = 10; i <= j; ++i, --j) // ^list separator ^comma operator std::cout << "i = " << i << " j = " << j << '\n'; // * in a return statement // return log("an error!"), -1; // * in an initializer expression // MyClass(const Arg& arg) // : member{ throws_if_bad(arg), arg } // etc. // comma operators can be chained; the result of the last // (rightmost) expression is the result of the whole chain: int n = 1; int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n); // m is now 6 std::cout << "m = " << (++m, m) << '\n'; }
出力
i = 0 j = 10 i = 1 j = 9 i = 2 j = 8 i = 3 j = 7 i = 4 j = 6 i = 5 j = 5 n = 2 m = 7
[編集] 条件演算子
条件演算子式は以下の形式をとります。
E1 ? E2 : E3 |
|||||||||
E1が評価され、文脈的にboolに変換されます。結果がtrueの場合、条件式の値はE2の値となります。そうでなければ、条件式の値はE3の値となります。
条件式E1 ? E2 : E3の型と値カテゴリは次のように決定されます。
[編集] ステージ1
E2とE3の両方がvoid型の場合、結果はvoid型の右辺値(C++11まで)prvalue(C++11以降)です。
E2とE3のいずれか一方がvoid型である場合
- void型であるそのオペランドが(括弧で囲まれている可能性のある)throw式である場合、結果はもう一方のオペランドの型と値カテゴリを持ちます[1]。もう一方のオペランドがビットフィールドである場合、結果もビットフィールドです。
- それ以外の場合、プログラムは不適格となります。
E2とE3のどちらもvoid型でない場合、次のステージに進みます。
2 + 2 == 4 ? throw 123 : throw 456; // the result is of type “void” 2 + 2 != 4 ? "OK" : throw "error"; // the result is of type “const char[3]” // even if an exception is always thrown
[編集] ステージ2
E2またはE3がlvalueビットフィールド(C++11まで)同じ値カテゴリのglvalueビットフィールド(C++11以降)であり、それぞれcv1 Tおよびcv2 Tの型である場合、残りのプロセスでは、オペランドはcv Tの型と見なされます。ここで、cvはcv1とcv2の結合です。
E2とE3が異なる型であり、以下のいずれかの条件が満たされている場合、ステージ3に進みます。
- E2とE3の少なくとも一方が(cv修飾されている可能性のある)クラス型である。
- E2とE3の両方が、cv修飾を除いて同じ型の左辺値(C++11まで)同じ値カテゴリと同じ型のglvalue(C++11以降)である。
それ以外の場合、ステージ4に進みます。
[編集] ステージ3
オペランド式X(型TX)から、オペランド式Y(型TY)に関連するターゲット型への暗黙的変換シーケンス[2]の構築が試みられます。具体的には以下の通りです。
- Yが左辺値の場合、ターゲット型は
TY&ですが、暗黙的変換シーケンスは参照が左辺値(C++11まで)glvalue(C++11以降)に直接バインドする場合にのみ形成できます。
|
(C++11以降) |
- Yが右辺値(C++11まで)prvalue(C++11以降)である場合、または上記の変換シーケンスのいずれも形成できない場合、かつ
TXとTYの少なくとも一方が(cv修飾されている可能性のある)クラス型である場合TXとTYが同じクラス型である場合(cv修飾を無視する)TYがTXと同じかそれ以上にcv修飾されている場合、ターゲット型はTYです。- それ以外の場合、変換シーケンスは形成されません。
- それ以外の場合、
TYがTXの基底クラスである場合、ターゲット型はTXのcv修飾子を持つTYです。 - それ以外の場合、ターゲット型はZの型です。ここでZは、lvalue-to-rvalue、array-to-pointer、およびfunction-to-pointerの標準変換を適用した後のYの値です。
- それ以外の場合、変換シーケンスは形成されません。
このプロセスを使用して、E2からE3に対して決定されたターゲット型への暗黙的変換シーケンスが形成できるかどうか、およびその逆が決定されます。
- 変換シーケンスを形成できない場合、次のステージに進みます。
- ちょうど1つの変換シーケンスが形成できる場合
- 変換シーケンスが曖昧な場合、プログラムは不正な形式です。
- それ以外の場合、選択された変換が選択されたオペランドに適用され、変換されたオペランドが元のオペランドの代わりとして残りのプロセスで使用され、次のステージに進みます。
- 両方のシーケンスが形成できる場合、プログラムは不正な形式です。
struct A {}; struct B : A {}; using T = const B; A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Target = const A
[編集] ステージ4
|
E2とE3が同じ型のlvalueである場合、結果はその型のlvalueであり、E2とE3の少なくとも一方がビットフィールドである場合はビットフィールドです。 |
(C++11まで) |
|
E2とE3が同じ型で同じ値カテゴリのglvalueである場合、結果は同じ型と値カテゴリを持ち、E2とE3の少なくとも一方がビットフィールドである場合はビットフィールドです。 |
(C++11以降) |
それ以外の場合、結果はrvalue(C++11まで)prvalue(C++11以降)です。
- E2とE3が同じ型でなく、いずれかが(cv修飾されている可能性のある)クラス型を持つ場合、ステージ5に進みます。
- それ以外の場合、ステージ6に進みます。
[編集] ステージ5
組み込み候補を使用して、オペランドを組み込み型に変換しようとオーバーロード解決が実行されます。
- オーバーロード解決が失敗した場合、プログラムは不正な形式です。
- それ以外の場合、選択された変換が適用され、変換されたオペランドが元のオペランドの代わりとして残りのプロセスで使用されます。次のステージに進みます。
[編集] ステージ6
配列からポインタへの変換と関数からポインタへの変換が(変換されている可能性のある)E2とE3に適用されます。これらの変換の後、以下の条件の少なくとも1つが満たされる必要があります。満たされない場合、プログラムは不正な形式です。
- E2とE3は同じ型を持つ。この場合、結果はその型であり、選択されたオペランドを使用してコピー初期化されます。
- E2とE3の両方が算術型または列挙型である。この場合、通常の算術変換が適用され、それらが共通の型に変換され、結果はその型となります。
- E2とE3の少なくとも一方がポインタである。この場合、lvalue-to-rvalue、ポインタ、関数ポインタ(C++17以降)、および修飾変換が適用され、それらを複合ポインタ型に変換し、結果はその型となります。
- E2とE3の少なくとも一方がメンバへのポインタである。この場合、lvalue-to-rvalue、メンバへのポインタ、関数ポインタ(C++17以降)、および修飾変換が適用され、それらを複合ポインタ型に変換し、結果はその型となります。
|
(C++11以降) |
int* intPtr; using Mixed = decltype(true ? nullptr : intPtr); static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int* struct A { int* m_ptr; } a; int* A::* memPtr = &A::m_ptr; // memPtr is a pointer to member m_ptr of A // memPtr makes nullptr as type of pointer to member m_ptr of A static_assert(std::is_same_v<decltype(false ? memPtr : nullptr), int*A::*>); // a.*memPtr is now just pointer to int and nullptr also becomes pointer to int static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
- ↑ この条件演算子は、C++14以前のC++11 constexprプログラミングで一般的に使用されていました。
- ↑ メンバアクセス、変換関数が削除されているかどうか(C++11以降)、およびオペランドがビットフィールドであるかどうかは無視されます。
|
条件演算子の結果型は、バイナリ型特性std::common_typeとしてもアクセスできます。 |
(C++11以降) |
[編集] オーバーロード
昇格された算術型LとRのすべてのペア、およびポインタ、メンバへのポインタ、またはスコープ付き列挙型であるすべての型Pについて、以下の関数シグネチャがオーバーロード解決に参加します。
| LR operator?:(bool, L, R); |
||
| P operator?:(bool, P, P); |
||
ここでLRは、LとRに対して実行される通常の算術変換の結果です。
演算子「?:」はオーバーロードできません。これらの関数シグネチャはオーバーロード解決のためだけに存在します。
#include <iostream> #include <string> struct Node { Node* next; int data; // deep-copying copy constructor Node(const Node& other) : next(other.next ? new Node(*other.next) : NULL) , data(other.data) {} Node(int d) : next(NULL), data(d) {} ~Node() { delete next; } }; int main() { // simple rvalue example int n = 1 > 2 ? 10 : 11; // 1 > 2 is false, so n = 11 // simple lvalue example int m = 10; (n == m ? n : m) = 7; // n == m is false, so m = 7 //output the result std::cout << "n = " << n << "\nm = " << m; }
出力
n = 11 m = 7
[編集] 標準ライブラリ
標準ライブラリの多くのクラスは、関数オブジェクトとして使用されるようにoperator()をオーバーロードしています。
| オブジェクトまたは配列を削除する ( std::default_delete<T>のpublicメンバ関数) | |
| 2つの引数の合計を返す ( std::plus<T>のpublicメンバ関数) | |
| 2つの引数の差を返す ( std::minus<T>のpublicメンバ関数) | |
| 2つの引数の積を返す ( std::multiplies<T>のpublicメンバ関数) | |
| 最初の引数を2番目の引数で除算した結果を返す ( std::divides<T>のpublicメンバ関数) | |
| 最初の引数を2番目の引数で除算した剰余を返す ( std::modulus<T>のpublicメンバ関数) | |
| 引数の否定を返す ( std::negate<T>のpublicメンバ関数) | |
| 引数が等しいかチェックする ( std::equal_to<T>のpublicメンバ関数) | |
| 引数が等しくないかチェックする ( std::not_equal_to<T>のpublicメンバ関数) | |
| 最初の引数が2番目よりも大きいかチェックする ( std::greater<T>のpublicメンバ関数) | |
| 最初の引数が2番目よりも小さいかチェックする ( std::less<T>のpublicメンバ関数) | |
| 最初の引数が2番目よりも大きいか等しいかチェックする ( std::greater_equal<T>のpublicメンバ関数) | |
| 最初の引数が2番目よりも小さいか等しいかチェックする ( std::less_equal<T>のpublicメンバ関数) | |
| 2つの引数の論理ANDを返す ( std::logical_and<T>のpublicメンバ関数) | |
| 2つの引数の論理ORを返す ( std::logical_or<T>のpublicメンバ関数) | |
| 引数の論理NOTを返す ( std::logical_not<T>のpublicメンバ関数) | |
| 2つの引数のビットごとのANDの結果を返す ( std::bit_and<T>のpublicメンバ関数) | |
| 2つの引数のビットごとのORの結果を返す ( std::bit_or<T>のpublicメンバ関数) | |
| 2つの引数のビットごとのXORの結果を返す ( std::bit_xor<T>のpublicメンバ関数) | |
| 保存された述語の呼び出し結果の論理補数を返す ( std::unary_negate<Predicate>のpublicメンバ関数) | |
| 保存された述語の呼び出し結果の論理補数を返す ( std::binary_negate<Predicate>のpublicメンバ関数) | |
| 保存された関数を呼び出す ( std::reference_wrapper<T>のpublicメンバ関数) | |
| ターゲットを呼び出す ( std::function<R(Args...)>のpublicメンバ関数) | |
| ターゲットを呼び出す ( std::move_only_functionのpublicメンバ関数) | |
| ターゲットを呼び出す ( std::copyable_functionのpublicメンバ関数) | |
| コルーチンの実行を再開する ( std::coroutine_handle<Promise>のpublicメンバ関数) | |
| このロケールの照合ファセットを使用して2つの文字列を辞書順に比較する ( std::localeのpublicメンバ関数) | |
value_type型の2つの値を比較する( std::map<Key,T,Compare,Allocator>::value_compareのpublicメンバ関数) | |
value_type型の2つの値を比較する( std::multimap<Key,T,Compare,Allocator>::value_compareのpublicメンバ関数) | |
| 関数を実行する ( std::packaged_task<R(Args...)>のpublicメンバ関数) | |
| エンジンの状態を進め、生成された値を返す ( std::linear_congruential_engine<UIntType,a,c,m>のpublicメンバ関数) | |
| (C++11) |
分布における次の乱数を生成する ( std::uniform_int_distribution<IntType>のpublicメンバ関数) |
コンマ演算子は、標準ライブラリのどのクラスによってもオーバーロードされていません。Boostライブラリは、boost.assign、boost.spirit、およびその他のライブラリでoperator,を使用しています。データベースアクセスライブラリSOCIもoperator,をオーバーロードしています。
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 446 | C++98 | 条件演算子のlvalue-to-rvalue変換について、一時オブジェクトが作成されるかどうかが未指定であった。 演算子がクラスrvalueを返す場合、常に一時オブジェクトが作成される。 |
常に一時オブジェクトを作成する 演算子がクラスrvalueを返す場合 |
| CWG 462 | C++98 | カンマ演算子の2番目のオペランドが一時オブジェクトである場合、 カンマ式の結果が参照にバインドされるときに、その寿命が延長されるかどうかは未指定であった。 カンマ式の結果 |
この場合の一時オブジェクト (そのため、その寿命は延長される) (そのため、その寿命は延長される) |
| CWG 587 | C++98 | 条件演算子の2番目と3番目のオペランドが、 cv修飾を除いて同じ型のlvalueである場合、 これらのオペランドがクラス型を持つ場合はlvalue、そうでない場合はrvalueであった。 それ以外の場合は右辺値 |
結果は常に この場合はlvalue |
| CWG 1029 | C++98 | デストラクタ呼び出しの型は未指定であった。 | voidとして指定された |
| CWG 1550 | C++98 | 他のオペランドが非voidである場合、 括弧で囲まれたthrow式は条件式で許可されていなかった。 |
受理された |
| CWG 1560 | C++98 | 条件演算子のvoidオペランドは、 他のオペランドに対して不必要なlvalue-to-rvalue変換を引き起こし、 常にrvalueをもたらした。 |
voidを持つ条件式はlvalueになり得る voidを含む条件式はlvalueになり得る |
| CWG 1642 | C++98 | 関数呼び出し式のfunction式は 関数ポインタlvalueとなることができた |
許可されなくなった。 |
| CWG 1805 | C++98 | 暗黙的変換シーケンスのターゲット型を決定する際、 YをZに変換する方法が不明確であった。 |
明確化された |
| CWG 1895 | C++98 C++11 |
削除された(C++11)またはアクセス不能な(C++98)変換関数が 条件式での変換を妨げるかどうか、および基底クラスから 派生クラスへのprvalueの変換が考慮されないかどうかが不明確であった。 派生クラスへのprvalueは考慮されなかった。 |
のように扱われる オーバーロード解決 |
| CWG 1932 | C++98 | 条件式で同型ビットフィールドが欠落していた | 基底型で処理される |
| CWG 2226 | C++11 | 条件演算子のもう一方のオペランドの ターゲット型を決定する際、そのオペランドがlvalueの場合、 参照がxvalueにバインドできなかった |
許可 |
| CWG 2283 | C++17 | 関数呼び出し演算子の型完全性要件が、P0135R1によって誤って削除された。 P0135R1 によって誤って削除された |
要件を復元した |
| CWG 2321 | C++98 | 条件演算子のもう一方のオペランドのターゲット型を決定する際、 派生クラス型が、cv修飾の少ない基底クラス型に変換できなかった。 よりcv修飾の少ない基底クラス型に変換できなかった |
基底クラス型への変換が許可された 派生クラスオペランドからのcv修飾子を持つ 派生クラスオペランドからの |
| CWG 2715 | C++98 | 各パラメータの初期化と破棄は、 呼び出し側関数のコンテキスト内で発生するとされていたが、 それは存在しない可能性がある[1] |
のコンテキスト内で発生する 囲む完全式 |
| CWG 2850 | C++98 | パラメータの破棄順序が不明確であった | 明確化された |
| CWG 2865 | C++98 | もし`TX`と`TY`が同じクラス型で、`TX`が`TY`よりも cv修飾されている場合、prvalue `Y`から暗黙的な変換シーケンスが依然として形成され得た。 prvalue Yから暗黙的な変換シーケンスが依然として形成され得た。 |
この場合、変換シーケンスは形成されない この場合は形成されない |
| CWG 2906 | C++98 | 条件演算子の場合、lvalue-to-rvalue変換は rvalue結果のケースで無条件に適用されていた |
特定の場合にのみ適用された |
- ↑ 例えば、関数は名前空間スコープ変数の初期化子で呼び出すことができますが、このコンテキストでは「呼び出し側関数」は存在しません。
[編集] 関連項目
| 共通の演算子 | ||||||
|---|---|---|---|---|---|---|
| 代入 | インクリメント デクリメント |
算術 | 論理 | 比較 | メンバ アクセス |
その他 |
|
a = b |
++a |
+a |
!a |
a == b |
a[...] |
関数呼び出し a(...) |
| コンマ a, b | ||||||
| conditional a ? b : c | ||||||
| 特殊な演算子 | ||||||
|
static_castは、ある型を関連する別の型に変換する | ||||||
| Cドキュメント(その他の演算子)
|