模板运算符<未调用

Template operator< not called

以下代码实现模板class Data,目的是比较其成员对象。其意图是,如果成员对象是同一类型,则使用该对象的operator<进行比较,如果成员对象是不同类型,则使用std::stringoperator<用于成员对象的字符串化。

#include <iostream>
#include <sstream>

template <typename T>
class Data
{
public:
  Data( const T& t ) : m_data( t ) {}
  template <typename U> bool operator<( const Data<U>& cu )
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return ( static_cast<std::ostringstream&>(( std::ostringstream().flush() << m_data )).str() <
             static_cast<std::ostringstream&>(( std::ostringstream().flush() << cu.m_data )).str() );
  }

#if 1 // Change this to "#if 0" and code doesn't work anymore.
  bool operator<( const Data<T>& ct )
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return ( m_data < ct.m_data );
  }
#endif

private:
  T m_data;
  template< typename U> friend class Data;

friend bool operator< <T> ( const Data<T>&, const Data<T>& );
};

template<typename T>
bool operator<( const Data<T>& a, const Data<T>& b )
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  return ( a.m_data < b.m_data );
}

int main( int argc, char* argv[] )
{
  Data<int> a(10);
  Data<std::string> b("2");
  Data<int> c(2);
  std::cout << "10 < \"2\"? " << std::boolalpha << ( a < b ) << std::endl;
  std::cout << "10 < 2? " << std::boolalpha << ( a < c ) << std::endl;
  return 0;
}

我正在试验成员 operator<() 与全局范围的 operator<()。前者在 #if 1 块中进行了演示,并且按预期工作。当我试图通过将 #if 1 更改为 #if 0 来强制使用后者时,代码不再按预期运行:全局范围 operator<() 似乎未被调用。

有人可以指出为什么全局范围的 operator<() 不是 invoked/does 不起作用,而成员 operator<() 可以吗?

这里发生的事情就是。我花了一段时间才找到发生这种情况的确切原因,尽管最后很简单。

当您考虑运算符重载时,会生成一个可行函数列表,然后将其传递给算法,该算法确定将调用其中的哪些函数。特别是对于运算符重载,考虑的函数列表是 this

16.3.1.2 表达式中的运算符 [over.match.oper/6]

The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates.

cppreference explains it in more detail

For a unary operator @ whose argument has type T1 (after removing cv-qualifications), or binary operator @ whose left operand has type T1 and right operand of type T2 (after removing cv-qualifications), three sets of candidate functions are prepared:

1) member candidates:

2) non-member candidates:

3) built-in candidates:

当重载决议发生时,成员函数会发生一些特殊的事情(强调我的)

If any candidate function is a member function (static or non-static), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.

所以基本上你为你的案例获得的重载是

template <typename U> 
bool operator<(EXACT_TYPE_PTR this, const Data<U>& cu );
template<typename T>
bool operator<(const Data<T>& a, const Data<T>& b)

其中 EXACT_TYPE_PTR 替换为您决定对其调用 operator< 的任何对象的类型。现在您有两个候选对象,一个是模板,另一个接受一个模板参数和一个模板实参。所以自然地,具有确切类型的那个是首选,因为它是 完全匹配


注意 您可能应该使成员 operator<() 函数为 const,这样它就可以更通用,甚至可以接受 const 参数。在当前情况下,如果您有一个 const Data 实例,那么将调用非成员重载