私はSOでC++のコンマ演算子のオーバーロードについて頻繁に質問を見ます(主にオーバーロード自体とは無関係ですが、シーケンスポイントの概念のようなもの)、それは私を不思議にさせます:
shouldの場合、コンマをオーバーロードしますか?その実用的な使用例は何ですか?
私が見た、または必要としているような頭の上の例は考えられません
foo, bar;
実際のコードでは、これが実際にいつ使用されるか(もしあれば)に興味があります。
強調を少し変更してみましょう。
いつyouコンマをオーバーロードしますか?
答え:決して。
例外:テンプレートメタプログラミングを行う場合、operator,
には演算子の優先順位リストの一番下に特別な場所があり、SFINAEガードなどの構築に役立ちます。
オーバーロードoperator,
は両方とも Boost にあります。
複数のインデックスを持つマップにインデックスを付けるために、コンマ演算子を使用しました。
enum Place {new_york, washington, ...};
pair<Place, Place> operator , (Place p1, Place p2)
{
return make_pair(p1, p2);
}
map< pair<Place, Place>, double> distance;
distance[new_york, washington] = 100;
Boost.Assign は、次のようなことを行うために使用します。
vector<int> v;
v += 1,2,3,4,5,6,7,8,9;
そして、私はそれが風変わりな言語のハッキングに使用されているのを見てきました。
ああ、これらの風変わりな使用法の1つを覚えています: 複数の式を収集する 。 (警告、ダークマジック。)
コンマには、void
型のパラメーターを取ることができるという興味深いプロパティがあります。その場合、組み込みのコンマ演算子が使用されます。
これは、式の型がvoidであるかどうかを判断する場合に便利です。
namespace detail_
{
template <typename T>
struct tag
{
static T get();
};
template <typename T, typename U>
tag<char(&)[2]> operator,(T, tag<U>);
template <typename T, typename U>
tag<U> operator,(tag<T>, tag<U>);
}
#define HAS_VOID_TYPE(expr) \
(sizeof((::detail_::tag<int>(), \
(expr), \
::detail_::tag<char>).get()) == 1)
読者に運動として何が起こっているかを理解させます。 operator,
は右側に関連付けられます。
@ GMan'sBoost.Assign と同様に、 Blitz ++ は、コンマ演算子をオーバーロードして 便利な構文 を処理します多次元配列。例えば:
Array<double,2> y(4,4); // A 4x4 array of double
y = 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
SOCI-C++データベースアクセスライブラリ では、インターフェイスのインバウンド部分の実装に使用されます。
sql << "select name, salary from persons where id = " << id,
into(name), into(salary);
根拠のFAQ から:
Q:オーバーロードされたコンマ演算子は難読化にすぎません、私はそれが好きではありません。
さて、次のことを考慮してください。
"クエリXをサーバーYに送信し、結果を変数Zに入れます。"
上記では、「and」がコンマの役割を果たします。 C++でコンマ演算子のオーバーロードがあまり一般的ではない場合でも、一部のライブラリはこれを行い、簡潔で習得しやすい構文を実現します。 SOCIでは、カンマ演算子が優れた効果でオーバーロードされたと確信しています。
可能性の1つは、 Boost Assign ライブラリです(ただし、一部の人々はこの使用法を適切な使用よりもむしろ考慮すると確信しています)。
Boost Spirit おそらくカンマ演算子もオーバーロードします(他のほとんどすべてをオーバーロードします...)
同じ行に沿って、コンマ演算子の過負荷を伴うgithub pullリクエストが送信されました。次のように見えた
class Mylogger {
public:
template <typename T>
Mylogger & operator,(const T & val) {
std::cout << val;
return * this;
}
};
#define Log(level,args...) \
do { Mylogger logv; logv,level, ":", ##args; } while (0)
それから私のコードで私ができること:
Log(2, "INFO: setting variable \", 1, "\"\n");
誰かがこれが良いまたは悪い使用例である理由を説明できますか?
実用的な使用法の1つは、マクロ内の変数引数で効果的に使用することです。ところで、変数引数は以前はGCCの拡張機能でしたが、現在はC++ 11標準の一部です。
A
型のオブジェクトを追加するclass X
があるとします。つまり.
class X {
public: X& operator+= (const A&);
};
A
の1つ以上のオブジェクトをX buffer;
に追加する場合はどうなりますか?
例えば、
#define ADD(buffer, ...) buffer += __VA_ARGS__
上記のマクロを使用する場合:
ADD(buffer, objA1, objA2, objA3);
次に展開されます:
buffer += objA1, objeA2, objA3;
したがって、これはコンマ演算子を使用した完璧な例になります。変数の引数は同じもので拡張されます。
したがって、これを解決するには、以下のようにcomma
演算子をオーバーロードし、+=
で囲みます
X& X::operator, (const A& a) { // declared inside `class X`
*this += a; // calls `operator+=`
}
ログ出力の印刷にはコンマ演算子を使用します。実際にはostream::operator<<
と非常に似ていますが、実際にはコンマ演算子betterがタスクにあります。
ので、私は持っています:
template <typename T>
MyLogType::operator,(const T& data) { /* do the same thing as with ostream::operator<<*/ }
これらの素敵なプロパティがあります
コンマ演算子の優先度は最低です。したがって、式をストリーミングしたい場合、括弧を忘れても事態は混乱しません。比較する:
myLog << "The mask result is: " << x&y; //operator precedence would mess this one up
myLog, "The result is: ", x&y;
比較演算子を問題なく内部に混在させることもできます。
myLog, "a==b: ", a==b;
カンマ演算子は視覚的には小さいです。多くのものを一緒に接着するとき、それは読書を台無しにしません
myLog, "Coords=", g, ':', s, ':', p;
カンマ演算子の意味、つまり「これを印刷」してから「それを印刷」に合わせます。
以下にOpenCVのドキュメントの例を示します( http://docs.opencv.org/modules/core/doc/basic_structures.html#mat )。カンマ演算子は、cv :: Matの初期化に使用されます。
// create a 3x3 double-precision identity matrix
Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);