【发布时间】:2025-12-05 07:10:01
【问题描述】:
我要实现一个类模板:
template <typename Type>
class MyClass {/*...*/}
我想强制MyClass 接受的Type 重载< 运算符。我怎样才能做到这一点?这应该是可能的,因为标准库正在为已排序的容器执行此操作。我不想使用显式模板专业化并在MyClass 中实现特定类型。我希望Type 负责。
【问题讨论】:
我要实现一个类模板:
template <typename Type>
class MyClass {/*...*/}
我想强制MyClass 接受的Type 重载< 运算符。我怎样才能做到这一点?这应该是可能的,因为标准库正在为已排序的容器执行此操作。我不想使用显式模板专业化并在MyClass 中实现特定类型。我希望Type 负责。
【问题讨论】:
简单的方法就是使用它。 C++ 模板完全是鸭子类型的,在您传入实际类型之前不会进行检查。
您可以使用基于 SFINAE 的技术来测试类似的东西...
namespace details {
template<template<class...>class Z, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider {using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
这是一个 SFINAE 帮助器,用于检测您是否可以将模板应用于一组类型。
template<class X, class Y=X>
using raw_less_r = decltype( std::declval<X>()<std::declval<Y>() );
template<class X, class Y=X>
using can_raw_less = can_apply<raw_less_r, X, Y>;
如果int < int 有效,则can_raw_less<int> 派生自true_type,否则false_type。
然后我们开始:
template <typename Type, class=void>
class MyClass;
template <typename Type>
class MyClass<Type, std::enable_if_t<can_raw_less<Type>>>
{/*...*/}
使用 SFINAE 和 enable_if_t 创建一个当且仅当 Type<Type 有效时才存在的特化。
请注意,更好的模式可能是:
template<class F, class...Args>
using invoke_result = decltype( std::declval<F>()( std::declval<Args>()... ) );
template<class F, class...Args>
using can_invoke = can_apply< invoke_result, F, Args... >;
template<class Type, class Cmp=std::less<Type>>
class MyClass {
static_assert( can_invoke< Cmp, Type const&, Type const& >{}, "You must be able to compare Type to itself" );
// use Cmp{}(Type, Type) to compare
};
这就是标准容器的作用,并添加了static_assert 以获取更清晰的错误消息。
【讨论】:
boost::has_less。
如果MyClass 的实现对Type 的对象使用< 运算符,则您无需执行任何操作。如果不支持该操作,编译器会报错。
如果MyClass 的实现不对Type 的对象使用< 运算符,则无需担心。
在任何一种情况下,除了记录Type 的预期之外,您无需做任何事情。
【讨论】:
operator<。但是,可以通过使用可以使用不同逻辑的自定义 clas 来使用不同的方法对容器的项目进行排序,例如 std::greater。
<,如果不支持则生成错误,而不是如果您不明确执行则会出现错误。
使用来自Boost.TypeTraits 的boost::has_less 简单易读:
#include <boost/type_traits/has_less.hpp>
template <typename Type>
class MyClass {
static_assert( boost::has_less<Type>::value,
"Template parameter Type must be less-than-comparable" );
};
【讨论】: