我正在编写一个仅使用标头的C ++库,它使用了很多模板。现在我想添加一些概念检查来处理在模板参数中使用不正确类型时引发的编译时错误。
例如,我需要类似指针的对象的概念,它可以指向单个对象(如std :: shared_ptr),类指针对象可以指向一个数组(通过operator [])但不能用于指针算术(如std :: unique_ptr),以及可用于指针算术的指针等。
由于概念仍然没有标准且编译器不支持,我需要自己实现。我知道Boost Concept库,但出于某种原因我不想将它添加到依赖项中。
所以问题是,如何实现某些类型要求的检查?它是如何在Boost中实现的?在这种情况下常见的技术是什么?
答案 0 :(得分:4)
最重要的概念检查可以说是enable_if
:它是一个模板,如果第一个模板参数是true
,它会提供给定的返回类型,并导致替换失败参数为false
:
//this one gets called only for pointers
template <typename T>
typename enable_if<is_pointer<T>::value, bool>::type do_stuff(T) {}
//this one gets called only for non-pointers
template <typename T>
typename enable_if<not is_pointer<T>::value, bool>::type do_stuff(T) {}
如果您不关心能够超载此类内容而且您喜欢可读的错误消息,则应使用static_assert
代替:
template <typename T>
class pointer_thingy {
static_assert(is_pointer<T>::value, "T must be a pointer");
//...
};
现在,对于更困难的部分:定义自己的概念模板事物。如果可能,最好的方法是根据上面链接中已有的标准方法编写它们。但是,有时您需要检查那里不可用的内容,例如特定操作的可用性。在这种情况下,SFINAE是你的朋友:
template <typename T>
class is_equality_comparable {
template <typename U> static auto check(const U& u) -> typename std::conditional<
std::is_convertible<decltype(u == u), bool>::value,
std::true_type, std::false_type>::type;
static std::false_type check(...);
public:
static constexpr bool value = decltype(check(std::declval<T>()))::value;
};
检查特定类型是否定义了相等运算符(operator==
),并返回可以用作bool
的内容。它如何做到这一点需要一些解释:这个类的主要功能是定义一个永远不会调用的check
方法,并通过计算check
的返回来生成正确的值类型。在底部,该类只是这样:它使用类型check
的虚数值(通过T
生成)来确定declval
的返回类型,以避免依赖于构造函数) 。为了使其正常工作,提供了check
的两个重载:第一个是模板化的,第二个使用...
表示法,以便接受任何参数并具有比第一个更低的选择优先级超载。第一个重载使用后缀返回类型,以便它可以引用其参数(这使得代码很多更清晰)并使用conditional
在true_type
和{{之间进行选择1}}基于false_type
是否正确返回可用作operator==
的内容。如果bool
不存在,则第一次重载导致替换失败,并且SFINAE确保从可能的重载列表中悄悄地丢弃它,这意味着对operator==
的假设调用将回退到第二个重载只返回check
。
当然,这只是我这样做的方式;这是一种有效的方法,但我不确定这是Boost的做法,或者就此而言,是否是其他人的做法。如果您能够使用具有真实概念支持的较新版本的C ++,那么您肯定应该使用它:除了其他不错的功能之外,如果您做错了什么,您将能够获得可理解的错误消息,不一定是你从上面提到的方法中得到的东西。最后要注意的是,如果你真的决定做这样的事情,那么严格的测试是至关重要的:当你的课程已经在你的其他地方使用过时,它很容易出错并且很难弄清楚如何修复它代码。