c++templatestype-conversioncopy-constructorcopy-initialization

Ambiguous Class Template Conversion


How would one add a template constructor to the class so that copy initialization from complex to complex is performed explicitly and without ambiguity? Is there a solution that is compiler and C++ version/standard agnostic? Is there an approach that only requires definition of a constructor without an additional operator overload?

I included the template copy constructor and operator overload (last two methods defined in the class) but the compiler gives me the following message.

Compilation error
main.cpp: In function ‘void testTemplateConstructor()’:
main.cpp:74:27: error: conversion from ‘complex<float>’ to ‘complex<double>’ is ambiguous
      complex<double> cd = cf;
                           ^~
main.cpp:35:5: note: candidate: complex<T>::operator complex<X>() [with X = double; T = float]
     operator complex<X>(){
     ^~~~~~~~
main.cpp:29:5: note: candidate: complex<T>::complex(complex<X>&) [with X = float; T = double]
     complex(complex<X>& arg) {
     ^~~~~~~

This is the test case being utilized.

void testTemplateConstructor() {
     complex<float> cf{1.0f, 2.0f};
     complex<double> cd = cf;
    
     Assert(cf.real()==cd.real(), "Real parts are different.");
     Assert(cf.imag()==cd.imag(), "Imaginary parts are different.");
}
template <typename T> class complex{    
    
    private:
    typedef complex<T> complexi;
    T real_;
    T imag_;
    
    public:
    complex(){
        real_ = 0;
        imag_ = 0;
    }
    complex(T a, T b){
        real_ = a;
        imag_ = b;
    }
    complex(T a){
        real_ = a;
    }
    complex(complex<T>& comp){
        real_ = comp.real_;
        imag_ = comp.imag_;
    }
  template <typename X>
    complex(complex<X>& arg) { 
        real_ = arg.real_;
        imag_ = arg.imag_;
    }
   
    template <typename X>                  
    operator complex<X>(){
        return complex<T>();
    }
};

Solution

  • This is the solution I was looking for, this works with the C++11 standard and compiler versions as old as x86_64 gcc 4.7.1 and clang 3.4.1. The only difference apart from the use of static_cast is the use of getters.

    using namespace std;
    
    template <typename T> class complex{    
        
        public:
        typedef complex<T> complexi;
        T real_;
        T imag_;
        
        public:
        complex(){
            real_ = 0;
            imag_ = 0;
        }
        complex(T a, T b){
            real_ = a;
            imag_ = b;
        }
        complex(T a){
            real_ = a;
        }
        complex(const complex<T>& comp){
            real_ = comp.real_;
            imag_ = comp.imag_;
        }
    
        template <typename X>
        complex(const complex<X>& rhs){
            real_ = static_cast<T>(rhs.real());
            imag_ = static_cast<T>(rhs.imag());
        }
    
        T real() const {
            return real_;
        }
        T imag() const {
            return imag_;
        }
    
      
    };