수색…


소개

C ++에서는 사용자 정의 유형에 대해 +-> 연산자를 정의 할 수 있습니다. 예를 들어, <string> 헤더는 문자열을 연결하는 + 연산자를 정의합니다. 이는 operator 키워드를 사용하여 연산자 함수 를 정의하여 수행됩니다.

비고

기본 제공 유형의 연산자는 변경할 수 없으며 연산자는 사용자 정의 유형에 대해서만 오버로드 할 수 있습니다. 즉, 적어도 하나의 피연산자는 사용자 정의 유형이어야합니다.

다음 연산자 오버로드 할 수 없습니다 .

  • 멤버 액세스 또는 도트 연산자 .
  • 멤버 액세스 연산자에 대한 포인터 .*
  • 범위 분석 연산자 ::
  • 삼항 조건부 연산자, ?:
  • dynamic_cast , static_cast , reinterpret_cast , const_cast , typeid , sizeof , alignofnoexcept
  • 사전 처리 지시문 ### . 모든 유형 정보를 사용할 수 있기 전에 실행됩니다.

일부 사업자가 있습니다 당신해야하지 (시간의 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 ). 첫 번째 인수는 값으로 전달됩니다. 이유는 무엇입니까? 때문에

  1. 원래 객체를 수정할 수 없습니다 ( Object foobar = foo + bar; foo 를 수정하면 안된다 Object foobar = foo + bar; )
  2. 객체를 수정할 수 있어야하므로 const 만들 수 없습니다 ( operator+ 는 객체를 수정하는 operator+= 구현되기 때문에)

const& 전달하는 것은 옵션이지만, 전달 된 객체의 임시 복사본을 만들어야합니다. 값을 전달하면 컴파일러가 대신 처리합니다.


operator+= 는 체인을 연결할 수 있기 때문에 자체에 대한 참조를 반환합니다 (시퀀스 포인트로 인해 정의되지 않은 동작 인 동일한 변수를 사용하지 마십시오).

첫 번째 인수는 참조 (수정하고자 함)이지만 const 수정할 수 없으므로 const 가 아닙니다. 두 번째 인수는 수정해서는 안되며, 성능상의 이유는 const& (const 참조 전달은 값보다 빠릅니다)에 의해 전달됩니다.

단항 연산자

2 개의 단항 연산자를 오버로드 할 수 있습니다.

  • ++foofoo++
  • --foofoo--

오버로딩은 두 유형 ( ++-- ) 모두 동일합니다. 설명을 위해 아래로 스크롤하십시오.

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 적합하지 않은 경우 (예 : 개체의 전체 복사본 을 수행해야하는 경우) 할당 연산자가 오버로드되어야합니다.

대입 연산자 = 오버로딩하는 것은 쉽지만 몇 가지 간단한 단계를 따라야합니다.

  1. 자체 할당 테스트. 이 확인은 다음 두 가지 이유로 중요합니다.
    • 자체 할당은 불필요한 복사본이므로 수행하기에 적합하지 않습니다.
    • 다음 단계는 자체 할당의 경우에는 작동하지 않습니다.
  2. 이전 데이터를 정리하십시오. 이전 데이터는 새 데이터로 교체해야합니다. 이제 이전 단계의 두 번째 이유를 이해할 수 있습니다. 개체의 내용이 손상되면 자체 할당이 복사본을 수행하지 못합니다.
  3. 모든 회원을 복사하십시오. classstruct 대해 assigment 연산자를 오버로드하면 컴파일러에서 자동으로 생성되지 않으므로 다른 객체의 모든 멤버를 복사해야합니다.
  4. *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 ++에서 명명 된 컨테이너 추가 연산자를 생성합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow