仅在返回表达式有效时才启用模板

Gio*_*ani 0 c++ templates sfinae

我想std::ostream用这种风格写一个包装器:

#include <iostream>

struct OstreamWrapper {
  OstreamWrapper(std::ostream &out) : out(out) {}

  template< typename T >
  decltype(auto) operator<<(T &&arg) {
    return out << std::move< T >(arg);
  }

  std::ostream &out;
};

int main() {
  OstreamWrapper wrap(std::cout);
  wrap << "Hello, world!";  // This works
  wrap << std::endl;        // This does not work
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于它不起作用(例如)std::endl,因为(因为我得到它)std::endl被重载,并且编译器在评估模板时不知道如何解决重载.

我相信这种情况可以通过一些聪明的SFINAE来解决,但我找不到有用的东西.我想我需要类似"只有在cout << arg形成良好的表达时启用此模板",但我不知道如何表达.

例如,我试过这个:

  template< typename T,
            typename = decltype(out << arg) >
  decltype(auto) operator<<(T &&arg) {
    return out << std::move< T >(arg);
  }
Run Code Online (Sandbox Code Playgroud)

但这不行,因为然后评估模板表达式,arg尚未定义.

  template< typename T,
            typename = decltype(out << std::declval< T >()) >
  decltype(auto) operator<<(T &&arg) {
    return out << std::move< T >(arg);
  }
Run Code Online (Sandbox Code Playgroud)

这编译,但没有做我想要的,因为它需要T知道类型,而我的问题实际上在于确定如何重载其参数.

我也尝试了更多基于std::enable_ifand std::is_invocable和的模糊条件std::result_of,但是他们引入了许多我无法理解的错误,在这里总结所有尝试可能毫无意义.

有没有办法正确地做这件事?可能使用C++ 14,因此代码库仍然更加落后于compatbile,但如果C++ 17是必要的,那么它也是可以的.

Jar*_*d42 5

您可以向强制类型添加重载(因此选择了唯一可用的重载):

struct OstreamWrapper {
    explicit OstreamWrapper(std::ostream &out) : out(out) {}

    template< typename T >
    decltype(auto) operator<<(T &&arg) {
        return out << std::forward<T>(arg);
    }

    decltype(auto) operator<<(std::ostream& (&arg)(std::ostream&)) {
        return out << arg;
    }

    std::ostream &out;
};
Run Code Online (Sandbox Code Playgroud)

演示