根据某个功能是否存在启用模板

Enable a template depending on whether a certain function exists

我想设计一个模板,自动为所有 类 T 提供一个 operator<<(std::ostream&, const T&)T::print_to(std::ostream&) 存在并且可以被调用,这样我就可以将打印函数定义为成员函数(特别是利用虚拟调用)。

通过反复试验,我得出了这个结论:

template<typename T, typename = decltype(std::declval<T>().print_to(std::declval<std::ostream&>()))>
std::ostream &operator<<(std::ostream &s, const T &t) {
    t.print_to(s);
    return s;
}

它似乎有效,但由于我对 SFINAE 和这类技巧还很陌生,想知道是否有任何陷阱或可以改进的地方。我在https://ideone.com/uLJxac.

放了一个小测试台

如果可能的话,我想要一个 C++14 解决方案,因为我正在使用 C++14 代码库。但是,如果使用 C++17 允许更好的解决方案,那么我也对那个感兴趣。

编辑:

您的代码没有任何缺陷,很抱歉。但是这个答案使您能够编写更像 C++20 concept:

的代码
template <class T>
auto& operator << (std::ostream &out, const printable_t<T> &t)
{
    t.print_to(out);
    return out;
}

其实我在detector的基础上写了一个C++17concept_check库,可以这样用

有关 C++20concept 支持的更多信息,请查看以下 2:Constraints and concepts (since c++20) 约束和概念 (TS)

原回答:

std::experiment::is_detector 可以为您施展魔法。虽然它不在标准库中,但实现起来并不困难,link 给出了建议的实现。

在这里,我将向您介绍如何检测该功能以及我对 is_detected_v 的实现。

#include <type_traits>
#include <utility>
#include <ostream>

// For support for C++17 is not complete in many compiler, I also define void_t
template <class...> using void_t = void;

namespace impl {
template <class Default, class AlwaysVoid, template <class...> class Op, class ...Args>
struct detector: private std::false_type
{
    using std::false_type::value;
    using type = Default; 
};   
template <class Default, template <class...> class Op, class ...Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...>: private std::true_type
{
    using std::true_type::value;
    using type = Op<Args...>;
}; 
} // namespace impl

struct nonsuch {};

#define CONCEPT_T constexpr const static inline bool
template <template<class...> class Op, class ...Args>
CONCEPT_T is_detected_v = impl::detector<nonsuch, void, Op, Args...>::value;

// Detect whether print_to exists.    
template <class T>
using print_to_ret_t = decltype( std::declval<T>().print_to( std::declval<std::ostream&>() ) );

template <class T>
CONCEPT_T has_func_print_to_v = is_detected_v<print_to_ret_t, T>;

template <class T, std::enable_if_t< has_func_print_to_v<T> >>
using printable_t = T;

#undef  CONCEPT_T

您可以尝试为这段代码添加 C++14 支持。不会太难。 CONCEPT_T必须改为constexpr const static bool才能调整为C++14

在我看来,您在 operator<<() 中正确应用了 SFINAE;我没有看到您的解决方案中存在缺陷。

我建议使用另一个版本(C++11 兼容,C++14 也兼容)只是因为需要更少的打字

template <typename T>
auto operator<< (std::ostream & s, T const & t)
   -> decltype( t.print_to(s), s )
 {
   t.print_to(s);

   return s;
 }