模板重载解析和隐式转换 想尝试一下,我最近决定尝试在C++中实现模板化复数类。作为参考,我使用C++ 11标准库实现,我发现了,实现和我之间的区别。它指的是它们如何重载类中的+=运算符
在我的方法中,我基本上有一个+=方法,它能够处理模板重载解析和隐式转换 想尝试一下,我最近决定尝试在C++中实现模板化复数类。作为参考,我使用C++ 11标准库实现,我发现了,实现和我之间的区别。它指的是它们如何重载类中的+=运算符,c++,templates,overloading,implicit-conversion,C++,Templates,Overloading,Implicit Conversion,在我的方法中,我基本上有一个+=方法,它能够处理Complex+=Complex和Complex+=隐式转换为T并因此转换为Complex的任何其他类型。以下是类别声明,以供澄清: template <typename T> class Complex { public: constexpr Complex (T=T(), T=T()); // added for clarity template <typename U>
Complex+=Complex
和Complex+=隐式转换为T并因此转换为Complex
的任何其他类型。以下是类别声明,以供澄清:
template <typename T>
class Complex {
public:
constexpr Complex (T=T(), T=T()); // added for clarity
template <typename U>
constexpr Complex (const Complex <U>&);
template <typename U>
constexpr Complex (Complex <U>&&);
template <typename U>
Complex <T>& operator= (const Complex <U>&);
template <typename U>
Complex <T>& operator= (Complex <U>&&);
Complex& operator+= (const Complex<T>&);
Complex& operator-= (const Complex<T>&);
Complex& operator*= (const Complex<T>&);
Complex& operator/= (const Complex<T>&);
Complex& operator++ (void);
Complex operator++ (int);
Complex& operator-- (void);
Complex operator-- (int);
std::string to_string (void) const;
constexpr T real (void);
constexpr T img (void);
void real (const T&);
void img (const T&);
private:
T _m_real, _m_img;
template <typename U>
friend class Complex;
};
模板
阶级情结{
公众:
constexpr复合体(T=T(),T=T());//为清晰起见添加
模板
constexpr复合体(const复合体&);
模板
constexpr复合体(复合体&&);
模板
复数&运算符=(常数复数&);
模板
复数和运算符=(复数和&);
复数&运算符+=(常数复数&);
复数和运算符-=(常数复数和);
复数和运算符*=(常数复数和);
复数&运算符/=(常数复数&);
Complex&operator++(void);
复数运算符++(int);
复合运算符-(void);
复数运算符--(int);
std::字符串到_字符串(void)常量;
constexpr T real(void);
constexpr T img(无效);
无效实数(常数T&);
无效img(const T&);
私人:
真实的,真实的;
模板
朋友阶层情结;
};
但是,在标准库实现中,它们对运算符+=
使用了两个重载,一个重载采用复数
,另一个重载采用T
。就我所测试的而言,这两种实现似乎产生了相同的行为:在任意两个复杂类型之间进行加法,或者在一个复杂类型和另一个隐式转换为复杂内部类型的类型之间进行加法
因此,我的问题是:除了优化掉一个临时复合体之外,还有什么理由使用一个单独的
操作符+=(T)
吗?如果所有其他复杂类型都隐式转换为复杂类型,为什么要使用nestde模板呢?您正在比较的两个接口实际上是:
// 1
Complex<T>& operator+=(const Complex<T>&);
// 2
template <typename X>
complex<T>& operator+=(const complex<X>&);
complex<T>& operator+=(const T&);
//1
复数&运算符+=(常数复数&);
// 2
模板
复数&运算符+=(常数复数&);
复数&运算符+=(常数T&);
两个不同之处在于,在2中,采用复数
的重载是以参数类型为模板的,并且存在直接采用T
的显式重载。由于现有的(假设您添加了缺少的构造函数[*])转换,这两种方法基本上都允许编译相同的代码,但需要额外的成本
如果您只想向复数的实部添加一个值,则第一种设计需要创建一个复数
(假设您拥有该构造函数,未显示),而在第二种情况下,不需要创建临时对象
类似地,将复杂
类型的不同实例化作为参数,隐式转换将在1中用于创建一个临时变量,然后添加该临时变量,而在第二种方法中,参数将直接绑定,而不需要临时变量
虽然这对于基本类型并不重要,但如果用于实例化模板的类型是用户定义的,并且构造/复制成本较高,则可能会出现这种情况。例如,考虑动态分配内存的<代码> BigNu/Cux>类型。临时文件将导致内存分配和释放
[*]您不允许默认构造或使用单个参数构造
struct Foo {
operator int()const {
return 7;
}
};`
按Foo
递增时会出现问题,因为只会调用一个用户定义的转换
例如:
Complex<double> d;
Foo f;
d += f; // fails to compile with your version, compiles with C++ standard one
compled;
福福;
d+=f;//无法编译版本,用C++标准编译
还有一个区别。如果我们有一个类<代码> Bar <代码>,它有一个<代码>操作符Cuffor()const ,它将不能使用C++标准版本。
在您的版本中,如果T
正好是double
,它就可以工作
简言之,您的参数可以隐式转换为特定类型或从特定类型转换为特定类型与接受该类型的参数不同。只能尝试一种用户定义的转换,因此,如果您采用可以转换为int
的int类型,并且采用可以从int
生成的类型,则并非所有可以转换为int
的类型都是可接受的
非用户定义的转换不受相同方式的限制。complex&operator+=(const T&T)
仅将T
添加到复数的Re部分。实际上,t
是一个复数,Im部分等于0
。这同样适用于复数&运算符-=(常量T&T)
。您可以在complex
标题中自己查看。您的from scalar ctor的签名丢失了吗?ctor中是否缺少一个=0
?是的,我在编辑中修复了这个问题。它在实现中,没有将它添加到声明中。很可能是错误的主题。因此,考虑到标准库更详细的重载提供了更明确的行为,您是否建议不要使用我的实现?@vitiv,我不确定。我发现标准中缺少CanImplicitlyConvertTo
重载是有问题的。我发现你的中缺少
重载是有问题的。在C++1y中,这两种方法都变得更加简单。希望该标准是基于实际的用例和基于它们的决策。。。检查复杂项目的历史记录
提案!忘了在帖子中提到,复数(T,T)将T()作为虚部的默认参数,因此向复数添加实数不需要显式