수색…
소개
C ++에서는 사용자 정의 유형에 대해 +
및 ->
연산자를 정의 할 수 있습니다. 예를 들어, <string>
헤더는 문자열을 연결하는 +
연산자를 정의합니다. 이는 operator
키워드를 사용하여 연산자 함수 를 정의하여 수행됩니다.
비고
기본 제공 유형의 연산자는 변경할 수 없으며 연산자는 사용자 정의 유형에 대해서만 오버로드 할 수 있습니다. 즉, 적어도 하나의 피연산자는 사용자 정의 유형이어야합니다.
다음 연산자 는 오버로드 할 수 없습니다 .
- 멤버 액세스 또는 도트 연산자
.
- 멤버 액세스 연산자에 대한 포인터
.*
- 범위 분석 연산자
::
- 삼항 조건부 연산자,
?:
-
dynamic_cast
,static_cast
,reinterpret_cast
,const_cast
,typeid
,sizeof
,alignof
및noexcept
- 사전 처리 지시문
#
및##
. 모든 유형 정보를 사용할 수 있기 전에 실행됩니다.
일부 사업자가 있습니다 당신해야하지 (시간의 99.98 %) 과부하 :
-
&&
및||
(대신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
를 수정하면 안된다Object foobar = foo + bar;
) - 객체를 수정할 수 있어야하므로
const
만들 수 없습니다 (operator+
는 객체를 수정하는operator+=
구현되기 때문에)
const&
전달하는 것은 옵션이지만, 전달 된 객체의 임시 복사본을 만들어야합니다. 값을 전달하면 컴파일러가 대신 처리합니다.
operator+=
는 체인을 연결할 수 있기 때문에 자체에 대한 참조를 반환합니다 (시퀀스 포인트로 인해 정의되지 않은 동작 인 동일한 변수를 사용하지 마십시오).
첫 번째 인수는 참조 (수정하고자 함)이지만 const
수정할 수 없으므로 const
가 아닙니다. 두 번째 인수는 수정해서는 안되며, 성능상의 이유는 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
가 아닌 이유이기도합니다 (달리 수정할 수는 없습니다).
후위 연산자는 임시 값 (이전 값)을 값으로 반환하므로 참조가 될 수 없습니다. 일시 변수에 대한 참조 일 것이므로 임시 변수가 사라지기 때문에 함수 끝의 가비지 값이됩니다 범위의). 또한 직접 수정할 수도 있기 때문에 const
될 수 없습니다.
첫 번째 인수는 "호출"객체에 대한 비 const
참조입니다. 왜냐하면 const
인 경우이를 수정할 수 없으며 참조가 아닌 경우 원래 값을 변경하지 않기 때문입니다.
그것은 postfix 연산자 오버로드에 필요한 복사 때문에 for
루프에서 for
+ + ++ 대신 prefix ++를 사용하는 습관을 만드는 것이 좋습니다. for
루프 관점에서 볼 때 일반적으로 기능적으로는 동일하지만 접두사 ++를 사용하면 약간의 성능 이점이있을 수 있습니다. 특히 복사 할 멤버가 많은 "뚱뚱한"클래스의 경우 성능상의 이점이 있습니다. 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;
배열 첨자 연산자
배열 첨자 연산자 []
오버로드 할 수도 있습니다.
객체가 const
인 경우 []
로 반환되는 객체를 수정할 수 없기 때문에 항상 두 버전 ( const
및 not- const
버전)을 구현해야 합니다 (시간의 99.98 %).
인수는 전달한 const&
참조로 전달하는 값보다는 빠르지 때문 값 대신 const
작업자가 실수로 인덱스를 변경하지 않도록.
연산자는 참조에 의해 반환됩니다. 왜냐하면 설계 상 object []
return을 수정할 수 있기 때문입니다. 즉 :
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;
};
함수 호출 연산자
함수 호출 연산자 ()
오버로드 할 수 있습니다.
오버로딩은 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
배정 연산자
할당 연산자는 변수의 상태를 변경할 수 있으므로 가장 중요한 연산자 중 하나입니다.
class
/ struct
대해 assigment 연산자를 오버로드하지 않으면 컴파일러에서 자동으로 생성됩니다. 자동 생성 된 할당 연산자는 모든 멤버에서 대입 연산자를 호출하여 하나의 객체가 복사되도록합니다 한 번에 다른 회원에게. class
멤버쉽 할당이 class
/ struct
적합하지 않은 경우 (예 : 개체의 전체 복사본 을 수행해야하는 경우) 할당 연산자가 오버로드되어야합니다.
대입 연산자 =
오버로딩하는 것은 쉽지만 몇 가지 간단한 단계를 따라야합니다.
- 자체 할당 테스트. 이 확인은 다음 두 가지 이유로 중요합니다.
- 자체 할당은 불필요한 복사본이므로 수행하기에 적합하지 않습니다.
- 다음 단계는 자체 할당의 경우에는 작동하지 않습니다.
- 이전 데이터를 정리하십시오. 이전 데이터는 새 데이터로 교체해야합니다. 이제 이전 단계의 두 번째 이유를 이해할 수 있습니다. 개체의 내용이 손상되면 자체 할당이 복사본을 수행하지 못합니다.
- 모든 회원을 복사하십시오.
class
나struct
대해 assigment 연산자를 오버로드하면 컴파일러에서 자동으로 생성되지 않으므로 다른 객체의 모든 멤버를 복사해야합니다. -
*this
반환*this
. 연산자는 체인화를 허용하기 때문에 자체적으로 참조로 반환됩니다 (즉int b = (a = 6) + 4; //b == 10
).
//T is some type
T& operator=(const T& other)
{
//Do something (like copying values)
return *this;
}
참고 : 할당 된 객체는 변경되지 않아야하고, 참조에 의한 전달이 값보다 빠르기 때문에 operator=
가 실수로 수정하지 않는다는 것을 확인하기 위해 const&
가 other
를 전달합니다. 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::ostream
overload<<
(예 :std::cout
) -
std::istream
overloads>>
를 사용하여 기본 스트림에서 변수를 읽습니다 (예 :std::cin
).
class
/ struct
외부에서 "일반적으로"오버로드하려고 할 때와 비슷합니다. 단, 인수를 지정하는 것이 같은 유형이 아닌 경우는 예외입니다.
- 반환 형식은 체인화를 허용하기 위해 참조로 전달 된 오버로드 할 스트림 (예 :
std::ostream
)입니다 (Chaining :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;
재검색 된 복소수
아래 코드는 네 가지 기본 연산자 (+, -, * 및 /)를 다른 필드의 멤버와 함께 적용 할 때 언어의 형식 승격 규칙에 따라 기본 필드가 자동으로 승격되는 매우 간단한 복소수 형식을 구현합니다. (또 다른 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
객체를 반환합니다. 또한 named_invoke( lhs, append_t, rhs )
를 호출하기 위해 half_apply*rhs
를 오버로드합니다.
적절한 append_t
토큰을 만들고 적절한 서명의 ADL 친화적 인 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
있습니다.
*dot*
및 *cross*
곱 연산자를 쓰는 것도 명백한 용도입니다.
*
의 사용은 +
와 같은 다른 구분 기호를 지원하도록 확장 될 수 있습니다. delimeter precidence는 명명 된 연산자의 정확성을 결정합니다. extra ()
를 최소한으로 사용하여 물리 방정식을 C ++로 변환 할 때 중요 할 수 있습니다.
위의 라이브러리가 약간 변경되면 표준을 업데이트하기 전에 ->*then*
연산자 ->*then*
지원하고 std::function
확장하거나 모나드 ->*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 ++에서 명명 된 컨테이너 추가 연산자를 생성합니다.