C++
演算子のオーバーロード
サーチ…
前書き
C ++では、ユーザー定義型に対して+
や->
などの演算子を定義することができます。たとえば、 <string>
ヘッダーは、文字列を連結する+
演算子を定義します。これは、 operator
キーワードを使用してoperator
子関数を定義することによって行われます。
備考
組み込み型の演算子は変更できません。演算子はユーザー定義型に対してのみオーバーロードできます。すなわち、オペランドの少なくとも1つはユーザ定義型でなければならない。
次の演算子はオーバーロードできません。
- メンバアクセスまたはドットオペレータ
.
- メンバーアクセス演算子へのポインタ
.*
- スコープ解決演算子
::
- 三項条件演算子、
?:
-
dynamic_cast
、static_cast
、reinterpret_cast
、const_cast
、typeid
、sizeof
、alignof
、およびnoexcept
- 任意のタイプ情報が使用可能になる前に実行される前処理ディレクティブ(
#
および##
)。
いくつかの演算子があります(時間の99.98%)オーバーロードしないでください:
-
&&
と||
(代わりに、bool
への暗黙的な変換を使用することをおbool
) -
,
- アドレス演算子(単項
&
)
どうして?なぜなら、別のプログラマーが予期しないかもしれない演算子をオーバーロードするため、予期しない動作となります。
たとえば、ユーザーが&&
と||
定義したとします。これらの演算子の過負荷は短絡評価を失い、特別なシーケンス属性(C ++ 17)を失います。シーケンシングの問題,
オペレータの過負荷にも当てはまります。
算術演算子
すべての基本的な算術演算子をオーバーロードすることができます:
-
+
および+=
-
-
および-=
-
*
と*=
-
/
および/=
-
&
と&=
-
|
そして|=
-
^
および^=
-
>>
および>>=
-
<<
と<<=
すべての演算子のオーバーロードは同じです。 スクロールダウン
class
/ struct
外部へのオーバーロード:
//operator+ should be implemented in terms of operator+=
T operator+(T lhs, const T& rhs)
{
lhs += rhs;
return lhs;
}
T& operator+=(T& lhs, const T& rhs)
{
//Perform addition
return lhs;
}
class
/ struct
内部のオーバーロード:
//operator+ should be implemented in terms of operator+=
T operator+(const T& rhs)
{
*this += rhs;
return *this;
}
T& operator+=(const T& rhs)
{
//Perform addition
return *this;
}
注意: operator+
は、参照を返すことは意味がなく( 新しいオブジェクトを返します)、 const
値を返すこともなく(一般的にはconst
によって返されるべきではありません)、非const
値によって返されます。最初の引数は値渡しです、なぜですか?なぜなら
- 元のオブジェクトを変更することはできません(
Object foobar = foo + bar;
foo
を変更しないでください。意味がありません) - オブジェクトを変更する必要があるため、
const
作成することはできません(operator+
は、オブジェクトを変更するoperator+=
観点から実装されているためです)
const&
渡すとオプションになりますが、渡されたオブジェクトの一時的なコピーを作成する必要があります。値を渡すことによって、コンパイラはそれを行います。
operator+=
は、チェーンへの参照が可能であるため、自身への参照を返します(ただし、同じ変数を使用しないでください。シーケンスポイントのために未定義の動作になります)。
最初の引数は参照(変更したい)ですが、 const
は変更できません。 2番目の引数は変更しないでください。そのため、パフォーマンスの理由から、 const&
(const参照渡しは値渡しよりも早い)に渡されます。
単項演算子
2つの単項演算子をオーバーロードすることができます。
-
++foo
とfoo++
-
--foo
とfoo--
オーバーロードは両方のタイプ( ++
と--
)で同じです。 スクロールダウン
class
/ struct
外部へのオーバーロード:
//Prefix operator ++foo
T& operator++(T& lhs)
{
//Perform addition
return lhs;
}
//Postfix operator foo++ (int argument is used to separate pre- and postfix)
//Should be implemented in terms of ++foo (prefix operator)
T operator++(T& lhs, int)
{
T t(lhs);
++lhs;
return t;
}
class
/ struct
内部のオーバーロード:
//Prefix operator ++foo
T& operator++()
{
//Perform addition
return *this;
}
//Postfix operator foo++ (int argument is used to separate pre- and postfix)
//Should be implemented in terms of ++foo (prefix operator)
T operator++(int)
{
T t(*this);
++(*this);
return t;
}
注意:接頭辞演算子は、自身への参照を戻すため、接頭辞演算を続行できます。接頭辞演算子がオブジェクトを変更するので、最初の引数は参照です。これがconst
でない理由です(別の方法で変更することはできません)。
postfix演算子は一時的な値(前の値)を返します。一時的な変数への参照であるため、関数の最後にガベージ値が入ります範囲の)。また、直接変更することができなければならないので、 const
にすることもできません。
最初の引数は、 "呼び出し"オブジェクトに対する非const
参照です。これは、 const
場合は変更できないため、参照でない場合は元の値を変更しないためです。
これは、後置演算子のオーバーロードに必要なコピーがあるため、 for
ループでpostfix ++の代わりに接頭辞++を使用する方が習慣になる方が良いです。 for
ループの観点からはfor
通常は機能的には同等ですが、プレフィックス++を使用する場合、特に "fat"クラスで多くのメンバーをコピーしてパフォーマンスを向上させる可能性があります。 forループで接頭辞++を使用する例:
for (list<string>::const_iterator it = tokens.begin();
it != tokens.end();
++it) { // Don't use it++
...
}
比較演算子
すべての比較演算子をオーバーロードすることができます:
-
==
と!=
-
>
および<
-
>=
および<=
これらの演算子をすべてオーバーロードするための推奨される方法は、2つの演算子( ==
および<
)を実装し、残りを定義するためにそれらを使用することです。 スクロールダウン
class
/ struct
外部へのオーバーロード:
//Only implement those 2
bool operator==(const T& lhs, const T& rhs) { /* Compare */ }
bool operator<(const T& lhs, const T& rhs) { /* Compare */ }
//Now you can define the rest
bool operator!=(const T& lhs, const T& rhs) { return !(lhs == rhs); }
bool operator>(const T& lhs, const T& rhs) { return rhs < lhs; }
bool operator<=(const T& lhs, const T& rhs) { return !(lhs > rhs); }
bool operator>=(const T& lhs, const T& rhs) { return !(lhs < rhs); }
class
/ struct
内部のオーバーロード:
//Note that the functions are const, because if they are not const, you wouldn't be able
//to call them if the object is const
//Only implement those 2
bool operator==(const T& rhs) const { /* Compare */ }
bool operator<(const T& rhs) const { /* Compare */ }
//Now you can define the rest
bool operator!=(const T& rhs) const { return !(*this == rhs); }
bool operator>(const T& rhs) const { return rhs < *this; }
bool operator<=(const T& rhs) const { return !(*this > rhs); }
bool operator>=(const T& rhs) const { return !(*this < rhs); }
演算子は明らかに対応する操作に対してtrue
またはfalse
を示すbool
返します。
すべての演算子はconst&
で引数を取っています。なぜなら、演算子を行う唯一のことは比較だからです。オブジェクトを変更しないでください。 &
(参照)を渡すことは値よりも高速であり、演算子がそれを変更しないことを確認するために、それはconst
参照です。
class
/ struct
体内の演算子はconst
として定義されていることに注意してください。なぜなら、関数がconst
、コンパイラはオペレータが何も変更しないことを知らないので、 const
オブジェクトを比較することはできません。
変換演算子
型演算子をオーバーロードすると、型を暗黙的に指定された型に変換できます。
変換演算子は、 class
/ struct
定義する必要があります。
operator T() const { /* return something */ }
注意:オペレータがあるconst
できるようにするconst
オブジェクトを変換します。
例:
struct Text
{
std::string text;
// Now Text can be implicitly converted into a const char*
/*explicit*/ operator const char*() const { return text.data(); }
// ^^^^^^^
// to disable implicit conversion
};
Text t;
t.text = "Hello world!";
//Ok
const char* copyoftext = t;
配列添字演算子
配列添字演算子[]
オーバーロードすることもできます。
あなたはいつも (時間の99.98パーセント)2つのバージョン、実装する必要がありconst
とnot- const
オブジェクトがある場合ので、バージョンをconst
、によって返されるオブジェクト変更できないようにする必要があり[]
。
引数は値渡しではなくconst&
によって渡されます。これは、参照渡しが値渡しよりも高速であり、 const
が誤ってインデックスを変更しないためです。
演算子は参照によって戻されます。これは、設計上、オブジェクト[]
戻り値、つまり、
std::vector<int> v{ 1 };
v[0] = 2; //Changes value of 1 to 2
//wouldn't be possible if not returned by reference
あなたはclass
/ struct
中でだけオーバーロードすることができます:
//I is the index type, normally an int
T& operator[](const I& index)
{
//Do something
//return something
}
//I is the index type, normally an int
const T& operator[](const I& index) const
{
//Do something
//return something
}
複数の添字演算子[][]...
、プロキシオブジェクトを介して実現できます。単純な行 - 主成分クラスの次の例は、これを示しています:
template<class T>
class matrix {
// class enabling [][] overload to access matrix elements
template <class C>
class proxy_row_vector {
using reference = decltype(std::declval<C>()[0]);
using const_reference = decltype(std::declval<C const>()[0]);
public:
proxy_row_vector(C& _vec, std::size_t _r_ind, std::size_t _cols)
: vec(_vec), row_index(_r_ind), cols(_cols) {}
const_reference operator[](std::size_t _col_index) const {
return vec[row_index*cols + _col_index];
}
reference operator[](std::size_t _col_index) {
return vec[row_index*cols + _col_index];
}
private:
C& vec;
std::size_t row_index; // row index to access
std::size_t cols; // number of columns in matrix
};
using const_proxy = proxy_row_vector<const std::vector<T>>;
using proxy = proxy_row_vector<std::vector<T>>;
public:
matrix() : mtx(), rows(0), cols(0) {}
matrix(std::size_t _rows, std::size_t _cols)
: mtx(_rows*_cols), rows(_rows), cols(_cols) {}
// call operator[] followed by another [] call to access matrix elements
const_proxy operator[](std::size_t _row_index) const {
return const_proxy(mtx, _row_index, cols);
}
proxy operator[](std::size_t _row_index) {
return proxy(mtx, _row_index, cols);
}
private:
std::vector<T> mtx;
std::size_t rows;
std::size_t cols;
};
関数呼び出し演算子
関数呼び出しoperator ()
オーバーロードすることができます:
オーバーロードはclass
/ struct
内部で行う必要があります:
//R -> Return type
//Types -> any different type
R operator()(Type name, Type2 name2, ...)
{
//Do something
//return something
}
//Use it like this (R is return type, a and b are variables)
R foo = object(a, b, ...);
例えば:
struct Sum
{
int operator()(int a, int b)
{
return a + b;
}
};
//Create instance of struct
Sum sum;
int result = sum(1, 1); //result == 2
代入演算子
代入演算子は、変数のステータスを変更できるため、最も重要な演算子の1つです。
class
/ struct
ためにassigment演算子をオーバーロードしないと、自動的に生成された代入演算子は "メンバーワイズ割り当て"を実行します。つまり、すべてのメンバーの代入演算子を呼び出して1つのオブジェクトをコピーします他のメンバーには、時にはメンバー。たとえば、オブジェクトのディープコピーを実行する必要がある場合など、単純なメンバーワイズ割り当てがclass
/ struct
適していない場合は、代入演算子をオーバーロードする必要があります。
代入演算子=
オーバーロードは簡単ですが、いくつかの簡単な手順に従ってください。
- 自己割り当てのテスト。このチェックは、次の2つの理由から重要です。
- 自己割り当ては無駄なコピーなので、実行するのは理にかなっていません。
- 自己割り当ての場合は次のステップは機能しません。
- 古いデータを消去してください。古いデータは新しいデータに置き換えなければなりません。今度は、前のステップの第2の理由を理解することができます。オブジェクトのコンテンツが破棄された場合、自己割り当てによってコピーを実行できなくなります。
- すべてのメンバーをコピーします。
class
またはstruct
ためにassigment演算子をオーバーロードすると、コンパイラーによって自動的に生成されるわけではないので、他のオブジェクトからすべてのメンバーをコピーする必要があります。 - これを返す
*this
。演算子は連鎖を許しているので(つまり、int b = (a = 6) + 4; //b == 10
)、それ自体が参照によって返されます。
//T is some type
T& operator=(const T& other)
{
//Do something (like copying values)
return *this;
}
注: other
はconst&
によって渡されます。割り当てられるオブジェクトは変更しないでください。参照渡しは値よりも速く、 operator=
よりも誤って変更しないことを確認するためconst
です。
=
の左の値は常に class
/ struct
そのものなので、代入演算子はclass
/ struct
でのみオーバーロードすることができます。自由な関数としてそれを定義することはこの保証を持たず、そのために禁止されています。
class
/ struct
で宣言すると、左の値は暗黙的にclass
/ struct
そのものなので、問題はありません。
ビットごとのNOT演算子
ビット単位のNOT( ~
)のオーバーロードはかなり簡単です。 スクロールダウン
class
/ struct
外部へのオーバーロード:
T operator~(T lhs)
{
//Do operation
return lhs;
}
class
/ struct
内部のオーバーロード:
T operator~()
{
T t(*this);
//Do operation
return t;
}
注意: operator~
は値を参照するのではなく、新しい値(変更された値)を返さなければならないため、値によって戻ります(仮オブジェクトの参照となります。オペレータは終了する)。 const
ではなく、呼び出しコードが後でそれを変更できるようにする必要があります(つまり、 int a = ~a + 1;
可能でなければなりません)。
class
/ struct
内部では、一時オブジェクトを作成する必要があります。 this
は変更できないため、元のオブジェクトが変更される可能性があります。
I / O用のビットシフト演算子
演算子<<
と>>
は、一般に、「書き込み」演算子および「読み出し」演算子として使用されます。
-
std::ostream
オーバーロード<<
を使用して、変数を基になるストリームに書き込みます(例:std::cout
) -
std::istream
overloads>>
は、基底のストリームから変数へ読み込みます(例:std::cin
)
class
/ struct
外側でそれらを "通常"オーバーロードしたい場合は、引数の指定が同じ型でない点を除いて、これを行う方法は似ています:
- 戻り値の型は、連鎖(Chaining:
std::cout << a << b;
)を可能にするために、参照渡しでオーバーロードしたいストリーム(std::ostream
)std::cout << a << b;
。例:std::ostream&
-
lhs
は戻り値の型と同じになります -
rhs
は、オーバーロードを許可したい(つまりT
)、パフォーマンスの理由から値の代わりにconst&
によって渡されます(rhs
は変更しないでください)。例:const Vector&
。
例:
//Overload std::ostream operator<< to allow output from Vector's
std::ostream& operator<<(std::ostream& lhs, const Vector& rhs)
{
lhs << "x: " << rhs.x << " y: " << rhs.y << " z: " << rhs.z << '\n';
return lhs;
}
Vector v = { 1, 2, 3};
//Now you can do
std::cout << v;
複雑な数字の再訪
以下のコードは、基本的なフィールドが自動的に昇格される非常にシンプルな複素数型を実装しており、異なるフィールドのメンバーを持つ4つの基本演算子(+、 - 、*、および/)の適用下で、 (別のcomplex<T>
かスカラー型か)。
これは、テンプレートの基本的な使用と並行して演算子のオーバーロードをカバーする全体的な例を意図しています。
#include <type_traits>
namespace not_std{
using std::decay_t;
//----------------------------------------------------------------
// complex< value_t >
//----------------------------------------------------------------
template<typename value_t>
struct complex
{
value_t x;
value_t y;
complex &operator += (const value_t &x)
{
this->x += x;
return *this;
}
complex &operator += (const complex &other)
{
this->x += other.x;
this->y += other.y;
return *this;
}
complex &operator -= (const value_t &x)
{
this->x -= x;
return *this;
}
complex &operator -= (const complex &other)
{
this->x -= other.x;
this->y -= other.y;
return *this;
}
complex &operator *= (const value_t &s)
{
this->x *= s;
this->y *= s;
return *this;
}
complex &operator *= (const complex &other)
{
(*this) = (*this) * other;
return *this;
}
complex &operator /= (const value_t &s)
{
this->x /= s;
this->y /= s;
return *this;
}
complex &operator /= (const complex &other)
{
(*this) = (*this) / other;
return *this;
}
complex(const value_t &x, const value_t &y)
: x{x}
, y{y}
{}
template<typename other_value_t>
explicit complex(const complex<other_value_t> &other)
: x{static_cast<const value_t &>(other.x)}
, y{static_cast<const value_t &>(other.y)}
{}
complex &operator = (const complex &) = default;
complex &operator = (complex &&) = default;
complex(const complex &) = default;
complex(complex &&) = default;
complex() = default;
};
// Absolute value squared
template<typename value_t>
value_t absqr(const complex<value_t> &z)
{ return z.x*z.x + z.y*z.y; }
//----------------------------------------------------------------
// operator - (negation)
//----------------------------------------------------------------
template<typename value_t>
complex<value_t> operator - (const complex<value_t> &z)
{ return {-z.x, -z.y}; }
//----------------------------------------------------------------
// operator +
//----------------------------------------------------------------
template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x + b.x)>>
{ return{a.x + b.x, a.y + b.y}; }
template<typename left_t,typename right_t>
auto operator + (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a + b.x)>>
{ return{a + b.x, b.y}; }
template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x + b)>>
{ return{a.x + b, a.y}; }
//----------------------------------------------------------------
// operator -
//----------------------------------------------------------------
template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x - b.x)>>
{ return{a.x - b.x, a.y - b.y}; }
template<typename left_t,typename right_t>
auto operator - (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a - b.x)>>
{ return{a - b.x, - b.y}; }
template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x - b)>>
{ return{a.x - b, a.y}; }
//----------------------------------------------------------------
// operator *
//----------------------------------------------------------------
template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x * b.x)>>
{
return {
a.x*b.x - a.y*b.y,
a.x*b.y + a.y*b.x
};
}
template<typename left_t, typename right_t>
auto operator * (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a * b.x)>>
{ return {a * b.x, a * b.y}; }
template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x * b)>>
{ return {a.x * b, a.y * b}; }
//----------------------------------------------------------------
// operator /
//----------------------------------------------------------------
template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x / b.x)>>
{
const auto r = absqr(b);
return {
( a.x*b.x + a.y*b.y) / r,
(-a.x*b.y + a.y*b.x) / r
};
}
template<typename left_t, typename right_t>
auto operator / (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a / b.x)>>
{
const auto s = a/absqr(b);
return {
b.x * s,
-b.y * s
};
}
template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x / b)>>
{ return {a.x / b, a.y / b}; }
}// namespace not_std
int main(int argc, char **argv)
{
using namespace not_std;
complex<float> fz{4.0f, 1.0f};
// makes a complex<double>
auto dz = fz * 1.0;
// still a complex<double>
auto idz = 1.0f/dz;
// also a complex<double>
auto one = dz * idz;
// a complex<double> again
auto one_again = fz * idz;
// Operator tests, just to make sure everything compiles.
complex<float> a{1.0f, -2.0f};
complex<double> b{3.0, -4.0};
// All of these are complex<double>
auto c0 = a + b;
auto c1 = a - b;
auto c2 = a * b;
auto c3 = a / b;
// All of these are complex<float>
auto d0 = a + 1;
auto d1 = 1 + a;
auto d2 = a - 1;
auto d3 = 1 - a;
auto d4 = a * 1;
auto d5 = 1 * a;
auto d6 = a / 1;
auto d7 = 1 / a;
// All of these are complex<double>
auto e0 = b + 1;
auto e1 = 1 + b;
auto e2 = b - 1;
auto e3 = 1 - b;
auto e4 = b * 1;
auto e5 = 1 * b;
auto e6 = b / 1;
auto e7 = 1 / b;
return 0;
}
名前付き演算子
標準的なC ++演算子によって "引用"された名前付き演算子でC ++を拡張することができます。
最初に、ダースラインのライブラリから始めます。
namespace named_operator {
template<class D>struct make_operator{constexpr make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
これはまだ何もしません。
まず、ベクトルを追加する
namespace my_ns {
struct append_t : named_operator::make_operator<append_t> {};
constexpr append_t append{};
template<class T, class A0, class A1>
std::vector<T, A0> named_invoke( std::vector<T, A0> lhs, append_t, std::vector<T, A1> const& rhs ) {
lhs.insert( lhs.end(), rhs.begin(), rhs.end() );
return std::move(lhs);
}
}
using my_ns::append;
std::vector<int> a {1,2,3};
std::vector<int> b {4,5,6};
auto c = a *append* b;
ここでのコアは、 append_t:named_operator::make_operator<append_t>
型のappend
オブジェクトを定義することappend_t:named_operator::make_operator<append_t>
。
次に、左右で必要な型に対してnamed_invoke(lhs、append_t、rhs)をオーバーロードします。
ライブラリはlhs*append_t
オーバーロードし、一時的なhalf_apply
オブジェクトをhalf_apply
ます。また、 half_apply*rhs
をnamed_invoke( lhs, append_t, rhs )
を呼び出すためにオーバーロードします。
適切なappend_t
トークンを作成し、適切な署名のADLに優しいnamed_invoke
を実行するnamed_invoke
で、すべてがフックアップして動作します。
より複雑な例として、std :: arrayの要素を要素ごとに乗算したいとします。
template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&& f) {
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto indexer() { return indexer( std::make_index_sequence<N>{} ); }
namespace my_ns {
struct e_times_t : named_operator::make_operator<e_times_t> {};
constexpr e_times_t e_times{};
template<class L, class R, std::size_t N,
class Out=std::decay_t<decltype( std::declval<L const&>()*std::declval<R const&>() )>
>
std::array<Out, N> named_invoke( std::array<L, N> const& lhs, e_times_t, std::array<R, N> const& rhs ) {
using result_type = std::array<Out, N>;
auto index_over_N = indexer<N>();
return index_over_N([&](auto...is)->result_type {
return {{
(lhs[is] * rhs[is])...
}};
});
}
}
ライブの例 。
このエレメントワイズ配列コードは、タプル、ペアまたはCスタイルの配列、または可変長のコンテナでも機能するように拡張でき、長さが一致しない場合の処理方法を決定します。
また、要素単位の演算子型で、 lhs *element_wise<'+'>* rhs
を得ることもできlhs *element_wise<'+'>* rhs
。
*dot*
と*cross*
プロダクト演算子を書くことも明白です。
*
の使用は、 +
ような他の区切り文字をサポートするように拡張することができます。 delimeterの確度は、名前付き演算子の精度を決定します。これは、extra ()
の最小限の使用で物理式をC ++に変換するときに重要になる場合があります。
上記のライブラリを少し変更するだけで、標準の更新に先立って->*then*
演算子をサポートし、 std::function
を拡張したり、monadic ->*bind*
書く->*bind*
。ステートフルな名前付き演算子を使用することもできます。ここでは、 Op
を最終呼び出し関数に慎重に渡します。
named_operator<'*'> append = [](auto lhs, auto&& rhs) {
using std::begin; using std::end;
lhs.insert( end(lhs), begin(rhs), end(rhs) );
return std::move(lhs);
};
C ++で名前付きコンテナ追加演算子を生成する。