SFINAE 重载 operator<< 以调用 'print' 方法(如果存在)

SFINAE overload operator<< to call 'print' method if it exists

我一直在努力理解 SFINAE,并试图编写一个简单的重载运算符 <<,它将在包含此类方法的任何 class 上调用 'print' 方法。我通读了问题 Is it possible to write a template to check for a function's existence? 的答案并尝试写:

template<class T, class = decltype(void(std::declval<T>().print), std::true_type{})>
inline std::ostream &operator<<(std::ostream &out, const T &obj) {
    obj.print(out); return out; }

template<class T, class = decltype(void(std::declval<T>().print(std::declval<std::ostream &>())), std::true_type{})>
inline std::ostream &operator<<(std::ostream &out, const T &obj) {
    obj.print(out); return out; }

但这根本行不通——编译器似乎没有问题为任何类型实例化模板,所以当我尝试打印诸如字符串文字之类的东西时会出现大量 'ambiguous overload' 错误...

您的"function exists"表达不正确。试试这个:

template <typename T,
          typename = decltype(
               void(std::declval<T>().print(std::declval<std::ostream&>())),
                    std::true_type{})>  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
std::ostream & operator<<(std::ostream & out, const T & obj)
{
    obj.print(out);
    return out;
}

您也可以考虑这个备选方案:

template <typename T>
auto operator<<(std::ostream & out, const T & obj)
     -> decltype(obj.print(out), (void)0, out)
{
    obj.print(out);
    return out;
}

我不明白你的第二个类型参数是什么意思

class = decltype(void(std::declval<T>().print), std::true_type{})

的意思。这应该评估什么?

我认为您可以使用以下方法使其工作。

#include <iostream>
#include <type_traits>

template<typename T,
         typename = decltype(std::declval<const T>().print(std::cout))>
std::ostream&
operator<<(std::ostream& out, const T& obj)
{
  obj.print(out);
  return out;
}

struct A
{
  void
  print(std::ostream& out) const
  {
    out << "A";
  }
};

int
main()
{
  A a {};
  std::cout << "And the winner is: " << a << std::endl;
}

它会正确输出 And the winner is: A 但可能有一些我忽略的极端情况。

表达式

decltype(std::declval<const T>().print(std::cout))

将评估 return 类型的 print(std::ostream&) const 成员函数,如果声明了这样的函数,否则会出现类型错误。