根据某个功能是否存在启用模板
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++17
concept_check库,可以这样用
有关 C++20
中 concept
支持的更多信息,请查看以下 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;
}
我想设计一个模板,自动为所有 类 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++17
concept_check库,可以这样用
有关 C++20
中 concept
支持的更多信息,请查看以下 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;
}