用c++编写和检查自己的概念
Writing and checking your own concepts in c++
我正在编写一个只使用头的C++库,它大量使用模板。现在,我想添加一些概念检查,以处理在模板参数中使用不正确类型时引发的编译时错误。
例如,我需要可以指向单个对象的类似指针的对象的概念(如std::shared_ptr(,可以指向数组(通过运算符[](但不能用于指针算术的类似指针对象(如std::unique_ptr(,以及可以用于指针算术等的指针的概念
由于概念仍然不是标准的,编译器也不支持这些概念,所以我需要自己实现。我知道Boost概念库,但由于某些原因,我不想将其添加到依赖项中。
所以问题是,如何实现对某些类型需求的检查?它是如何在Boost中实现的?在这种情况下常见的技术是什么?
自从我还在使用C++11以来,我自己也做过一些类似的事情。从本质上讲,做到这一点的方法是大量使用SFINAE,并熟悉所有这些事情:http://en.cppreference.com/w/cpp/types
概念检查中最重要的可以说是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
的返回类型来生成正确的值。在底部,该类就是这样做的:当使用类型为T
的虚值(通过declval
生成以避免对构造函数的依赖(调用时,它确定check
的返回类型。为了使其正确工作,提供了check
的两个重载:第一个重载是模板化的,第二个重载使用...
表示法,以便接受任何参数,并具有比第一个重载更低的选择优先级。第一个重载使用后缀返回类型,这样它就可以引用它的参数(这使代码更干净(,并使用conditional
根据operator==
是否正确地返回了可以用作bool
的内容在true_type
和false_type
之间进行选择。如果operator==
不存在,则第一个重载会导致替换失败,并且SFINAE确保它从可能的重载列表中被悄悄丢弃,这意味着对check
的假设调用会回退到第二个重载,该重载只返回false_type
。
当然,这只是我这样做的方式;这是一种有效的方法,但我不确定Boost是这样做的,还是其他人也是这样做的。如果你能使用一个新版本的C++并支持真正的概念,你肯定应该使用它:除了其他不错的功能外,如果你做错了什么,你将能够获得可理解的错误消息,这不一定是你从上面提到的方法中得到的东西。最后要注意的是,如果你真的决定做这样的事情,严格的测试是至关重要的:当你的类已经在代码的其他地方使用时,很容易出错,很难找出如何修复它。
- 没有为自己的结构调用列表推回方法
- 在他自己的方法中,有可能将一个对象取消引用到另一个对象吗
- 在c++中为我自己的基于指针的数组分配内存的正确方法
- C++从对象自己的类中删除对象
- 使用 std::optional,而不是自己的结构
- 子轴围绕父轴而不是他自己的轴旋转
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- C++ 如何为自己的迭代器类从迭代器转换为const_iterator?
- 重载 + 自己的类和 std::string 的运算符
- 类无法访问自己的私有静态 constexpr 方法 - Clang bug?
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 如何访问模板参数自己的模板参数?
- 将矩阵乘以我自己的输入的向量
- 您应该在什么时候创建自己的异常类型
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- string1 == string2 和你自己的 for 循环比较有什么区别?
- 如何正确包含我自己的标头?
- 在编写自己的流运算符时,如何检查当前的 ostream dec/hex 模式?
- 尝试检查输入的文本与我自己的自定义答案
- 用c++编写和检查自己的概念