模棱两可的类模板转换

Ambiguous Class Template Conversion

提问人:Darnoc Eloc 提问时间:6/11/2022 更新时间:6/19/2022 访问量:147

问:

如何将模板构造函数添加到类中,以便显式地执行从复杂到复杂的复制初始化,而不会产生歧义?是否有与编译器和 C++ 版本/标准无关的解决方案?有没有一种方法只需要定义构造函数而不需要额外的运算符重载?

我包含模板复制构造函数和运算符重载(类中定义的最后两个方法),但编译器给了我以下消息。

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) {
     ^~~~~~~

这是正在使用的测试用例。

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>();
    }
};
C++ 模板 类型转换 构造函数 复制 初始化

评论


答:

1赞 user12002570 6/11/2022 #1

这样,从复杂到复杂之间的复制初始化是显式执行的,没有歧义?是否有与编译器和 C++ 版本/标准无关的解决方案?

是的,在这个特定示例中,您可以向 ctor 的参数添加一个低级别。const

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){
        std::cout<<"nornal version";;
        real_ = comp.real_;
        imag_ = comp.imag_;
    }
   template <typename X>
     complex(const complex<X>& arg) { 
        std::cout<<"template version";;
        real_ = arg.real_;
        imag_ = arg.imag_;
    }
   
   
};
void testTemplateConstructor() {
     complex<float> cf{1.0f, 2.0f};
     complex<double> cd = cf;
    
    
}

演示

评论

0赞 Goswin von Brederlow 6/11/2022
你甚至需要它们吗?他们不是在做同样的事情吗?
0赞 user12002570 6/11/2022
@GoswinvonBrederlow OP 在问题中没有提到完全删除其中一个也是一种选择。否则,这样做是消除歧义的明显方法。无论如何,我已经在答案的末尾添加了这一点,尽管我认为 OP 已经意识到了这一点。
0赞 Goswin von Brederlow 6/11/2022
我只是想知道是否有任何情况,您需要其中一个而另一个不起作用,反之亦然。有没有两者都需要的情况?
1赞 user12002570 6/12/2022
如果您在模板化构造函数中添加低级常量,它适用于所有编译器和所有 C++ 版本。请参阅使用 const 的工作演示。请注意,在上面的演示链接中,我添加了一个并用注释突出显示了它,以便于发现。我在回答中添加了相同的内容。看看我更新的答案。const
1赞 user12002570 6/14/2022
@DarnocEloc 通过添加 ctors 的参数,成为const 对象的 lvlaue 引用,这意味着它们现在可以绑定到相应类型的临时 demo。在没有参数之前,参数是对非常量对象的简单左值引用,这意味着在它们不能绑定到临时对象之前,你会得到同样错误。演示。另外,请注意,您应该删除 否则您可以进行无限递归。constconstoperator complex<X>()
0赞 Darnoc Eloc 6/19/2022 #2

这就是我一直在寻找的解决方案,它适用于 C++11 标准和编译器版本,x86_64 gcc 4.7.1 和 clang 3.4.1。除了使用static_cast之外,唯一的区别是 getter 的使用。

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_;
    }

  
};