模板和操作符重载

Template and operator overloading

本文关键字:重载 操作符      更新时间:2023-10-16

我正在检查代码…我发现了一个模板类,它的声明如下所示。

template <class T> 
class tType 
{
public:
    T value;
    T operator=(T val){ value = val; return value; }
    tType<T> operator=(tType<T> &val){ value = val.value; return *this; }
    operator T(){ return value; }
};

我还发现操作符重载,如下…

* * * * * * 1 * * * * * *

template <class T>
bool operator==(T& val, tType<T>& tval) { return(val == tval.value); }

我不明白上面的操作符何时被调用…因为当我…

int main(void)
{
    tType<int> x;
    x = 5;
    if (5 == x)
    {
        cout << "Both are equal" << endl;
    }
    return 0;
}

上面说的操作符没有被调用…我还在工作……我还编写了下面的代码用于测试,下面的代码片段被称为…

template<class T>
bool operator==(int x, tType<T>& val) { return (x == val.value); }

我不知道谁叫 1 * * * * * * * * * *

正确的是,您的operator==没有被调用的原因是缺少const标识符。在这种情况下,文字5不能作为参数T& val变量引用传递。但是,这并不是故事的全部。

正如你所指出的,即使函数没有被调用,代码仍然会编译和运行。

你的代码仍然工作的原因是由于转换操作符: operator T(){ return value; }

转换操作符允许从一种类型隐式强制转换为另一种类型。例如:

struct Number {
    Number(int x) : value(x) { }
    operator int() { return value; }
private:
    int value;
};
int main() {
    Number n(5);
    int x = n;          // 5  -- operator int() at work
    int y = n + x;      // 10 -- operator int() at work
    Number z = x + y;   // 15 -- Number(int) at work
    cout << x <<" "<< y <<" "<< z << endl;
    return 0;
}

这里,operator int()允许n转换成int赋值给x,再转换成intx相加(结果int再赋值给y),而一元构造函数Number(int)允许x + y的结果转换回Number

在您的代码中,5 == x仍然可以编译,因为tType<int> x通过其operator int()成员函数转换为int,然后将结果int与文字5进行比较。


然而,对于一个类来说,这可能不是一个很好的设计。例如,为了比较两个tType<T>,需要将其中一个转换为T
template <class T>
bool operator==(const T& val, const tType<T>& tval) {
    cout << "operator==(const T& val, const tType<T>& tval) was called." << endl;
    return(val == tval.value);
}
//...
cout << ( tType<int>() == tType<int>() ) << endl;   // calls operator==(const T&, const tType<T>&)

你宁愿用一个operator==代替两个tType<T>

另一个更大的问题是不是对称的:

cout << (x1 == 5) << endl;      // calls operator==(int,int)   (the built-in function)

呵!


要解决所有这些问题,有一个非常简单的设计解决方案。这是摘自Scott Meyers的书Effective C++ Third Edition(一本非凡的书)。

不是用转换操作符转换回T,类应该包含一个隐式的一元构造函数来将T转换为tType<T>,并且应该为tType<T>声明一个operator==:

template <class T> 
class tType 
{
public:
    tType<T>(const T &val) : value(val) { }       // unary non-explicit constructor
    tType<T> operator=(const tType<T> &val){ value = val.value; return *this; } // only need one
    T val() { return value; }
private:
    T value;          // Note also value is private (encapsulation)
};
template<class T>
bool operator==(const tType<T>& a, const tType<T>& b) { return ( a.val() == b.val() ); }

重要的变化:

  1. 添加了一个只接受一个值的构造函数;这允许将T s转换为tType s。
  2. 删除了使用T s的赋值运算符(因为现在我们可以将它们转换为tType s)。
  3. 只有一个相等运算符,需要两个const tType<T>& s,这意味着operator==可以处理T s和tType<T> s的任何组合(或者,更好的是,任何可转换为其中任何一个的东西)。

==操作符试图将非const引用绑定到参数-像"5"这样的值不允许这样做…参数应为const:

template <class T>
bool operator==(const T& val, const tType<T>& tval) { return val == tval.value; }

由于这仍然只支持5 == my_tType而不支持my_tType == 5,您可能还需要提供…

template <class T>
bool operator==(const tType<T>& tval, const T& val) { return val == tval.value; }

…对于my_tType1 == my_tType2

template <class T>
bool operator==(const tType<T>& tval1, const tType<T>& tval2) {
    return tval1.value == tval2.value; }

这使您可以最大限度地控制比较。另一种方法是允许从T参数隐式构造tType,但是隐式构造和转换操作符现在通常不被鼓励——它们可能导致歧义和意外创建难以调试的临时对象。另一个问题的主题....

当你说"上面说的操作符没有被调用…实际上发生的是,tType::operator T()(返回value;)被隐式调用,返回一个int,可以作为int5进行比较。因此,您进行了比较,但没有使用自定义operator==。这是为函数调用找到"最佳匹配"的正常结果。

修改函数

template <class T>
bool operator==(T& val, tType<T>& tval) { return(val == tval.value); }

template <class T>
bool operator==(T const& val, tType<T> const& tval) { return(val == tval.value); }

template <class T>
bool operator==(T val, tType<T> tval) { return(val == tval.value); }

您的函数没有被调用,因为5不能转换为int&