Интерпретировать примитивный тип данных как пользовательский класс - PullRequest
0 голосов
/ 10 ноября 2018

TLDR; Я хочу использовать операторы, определенные для моего класса, с примитивными типами данных, которые могут быть преобразованы в мой класс без приведения типов / инициализации новых переменных. т.е.

mycomplex x = 5 , y ;
y = x + 3 ;

В качестве побочного проекта я занимаюсь разработкой класса комплексных чисел. По понятным причинам любой примитивный числовой тип данных может интерпретироваться как комплексное число. Поэтому в любой операции, включающей примитивный тип и комплекс, я бы хотел, чтобы примитивный тип преобразовывался в комплекс и продолжал операцию, используя мои определенные операции, включающие два комплексных числа.

Каждый тип примитива может быть перегружен на каждом операторе, но порядок имеет значение, и с 4 базовыми операторами и несколькими более высокими математическими функциями он быстро превратится во много кода. Поэтому вопрос в том, как написать мой класс таким образом, чтобы примитивный тип "приводил" к сложному и выполнял обычные сложные операции.

Этот код компилируется, если закомментирована вторая-последняя строка main. Цель состоит в том, чтобы получить эту строку для mycomplex с cout 8 + 2i.

#include <iostream>

template <class T>
class mycomplex{
    private:
        T real ; 
        T imag ; 
    public:
        mycomplex( const mycomplex<T> & x ) ;
        mycomplex( const T & realx , const T & imagx ) ; 
        mycomplex( const T & x ) ; 
        mycomplex( ) ;

        template <class U>
        friend std::ostream & operator << ( std::ostream & os , const mycomplex<U> & x ) ;

        template <class U>
        friend mycomplex<U> operator + ( const mycomplex<U> & lhs , const mycomplex<U> & rhs ) ;
} ;

int main( int argc , char * argv[] ){
    mycomplex<float> x = 5 , y( 3 , 2 ) ; 
    mycomplex<float> z = y + x ; 
    std::cout << x << '\n' << y << '\n' << z << std::endl ;
    z = 5 + y ; 
    return 0 ;
}

template <class T>
mycomplex<T>::mycomplex( const mycomplex<T> & x ){
    real = x.real ;
    imag = x.imag ; 
}
template <class T>
mycomplex<T>::mycomplex( const T & realx , const T & imagx ){
    real = realx ; 
    imag = imagx ; 
}
template <class T>
mycomplex<T>::mycomplex( const T & x ){
    real = x ; 
    imag = 0 ;
}
template <class T>
mycomplex<T>::mycomplex( ){
    real = 0 ; 
    imag = 0 ; 
}
template <class T>
std::ostream & operator << ( std::ostream & os , const mycomplex<T> & x ){
    os << x.real ; 
    if( x.imag >= 0 ) 
        os << "+" ;
    os << x.imag << "i" ;
    return os ;
}
template <class T>
mycomplex<T> operator + ( const mycomplex<T> & lhs , const mycomplex<T> & rhs ){
    mycomplex<T> ans ;
    ans.real = lhs.real + rhs.real ; 
    ans.imag = lhs.imag + rhs.imag ; 
    return ans ; 
}

Я посмотрел на этот ответ и реализовал его, как вы можете видеть выше, но это не позволяет компилировать его, хотя это удобно при объявлении переменных. Я также посмотрел на этот ответ и добавил приведение типа к типу шаблона, но это заставляет мой класс перейти к шаблону, что в точности противоположно цели. Это позволяет скомпилировать приведенный выше код, но любой mycomplex + T дает mycomplex без мнимой части, что неверно.

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Вы можете использовать std :: enable_if, но это очень и очень уродливо.

Я тороплюсь, поэтому, к сожалению, я не тестировал это экстенсивно или включил рекламные акции (float + complex<int> дает вам complex<float>). Код запуска здесь .

#include <iostream>
#include <type_traits>

template <class T>
class mycomplex{
    private:
        T r ; 
        T i ; 
    public:
        using Underlying = T;
        mycomplex( const mycomplex<T> & x ) ;
        mycomplex( const T & realx , const T & imagx ) ; 
        mycomplex( const T & x ) ; 
        mycomplex( ) ;

        T real() const{
            return r;
        }
        T imag() const {
            return i;
        }

        template <class U>
        friend std::ostream & operator << ( std::ostream & os , const mycomplex<U> & x ) ;
} ;

template <class T>
mycomplex<T>::mycomplex( const mycomplex<T> & x ){
    r = x.r ;
    i = x.i ; 
}
template <class T>
mycomplex<T>::mycomplex( const T & realx , const T & imagx ){
    r = realx ; 
    i = imagx ; 
}
template <class T>
mycomplex<T>::mycomplex( const T & x ){
    r = x ; 
    i = 0 ;
}
template <class T>
mycomplex<T>::mycomplex( ){
    r = 0 ; 
    i = 0 ; 
}
template <class T>
std::ostream & operator << ( std::ostream & os , const mycomplex<T> & x ){
    os << x.r ; 
    if( x.i >= 0 ) 
        os << "+" ;
    os << x.i << "i" ;
    return os ;
}

template<typename T>
struct is_mycomplex : std::false_type{
};

template<typename T>
struct is_mycomplex<mycomplex<T>> : std::true_type{
};

static_assert(!is_mycomplex<int>::value);
static_assert(is_mycomplex<mycomplex<int>>::value);

template <typename T>
auto type_helper(){
    if constexpr (is_mycomplex<T>::value){
        return typename T::Underlying{};
    } else {
    return T{};
    }
}
template <class L, class R, typename = std::enable_if_t<
    is_mycomplex<L>::value + is_mycomplex<R>::value == 2
        ||
    (is_mycomplex<L>::value + is_mycomplex<R>::value == 1
     &&
     std::is_arithmetic_v<L> + std::is_arithmetic_v<R> ==1)
    >>
auto operator + ( const L& lhs , const R& rhs ){    
    using T = decltype(type_helper<L>());
    T real = 0; 
    T imag = 0;
    if constexpr(std::is_arithmetic_v<L>) {
        real+=lhs;
    } else {
        real+=lhs.real();
        imag+=lhs.imag();
    }
    if constexpr(std::is_arithmetic_v<R>) {
        real+=rhs;
    } else {
        real+=rhs.real();
        imag+=rhs.imag();
    }

    return mycomplex<T>(real, imag);
 ; 
}

int main(){
    mycomplex<float> x = 5 , y( 3 , 2 ) ; 
    mycomplex<float> z = y + x ; 
    std::cout << x << '\n' << y << '\n' << z << std::endl ;
    z = 5.5f + y ; 
    std::cout << "\n\n" << z << std::endl ;
}
0 голосов
/ 10 ноября 2018

Давайте посмотрим на эту строку:

z = 5 + y ; 

При y, равном mycomplex<float>, компилятор сначала ищет перегрузку operator+, принимая целое число (тип литерала 5 является целым числом) и mycomplex<float>. Этого не существует, только operator+ принимает два mycomplex значения.

Поскольку y уже имеет тип mycomplex, компилятор попытается преобразовать 5 в mycomplex<float>. Это было бы невозможно, поскольку вы можете конвертировать только значения float в mycomplex<float>.

На данный момент ясно, что нет доступных перегрузок и преобразований, которые бы сделали эту линию правильно сформированной. Компиляция прекращается.

Одним из возможных решений было бы создание оператора преобразования из что угодно в mycomplex и включение его только для числовых типов.

Примерно так:

template<class Z, std::enable_if_t<std::is_arithmetic_v<Z>>* = nullptr>
mycomplex(Z& z);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...