在C ++中用什么来替代概念(即将发布的功能)?
在C ++中用什么来替代概念(即将发布的功能)?
你可能听说过C ++中的概念。 这是一个功能,可以让你指定模板类型的需求。
我正在寻找一种方法来实现这一点,我发现的最好的方法是在Stroustrup的书中,他使用谓词和static_assert一起使用,如下所示:
template<typename Iter, typename Val>
Iter find(Iter b, Iter e, Val x)
{
static_assert(Input_iterator<Iter>(),"find(): Iter is not a Forward iterator");
// Rest of code...
}
请告诉我,如果您使用其他方法或者如果这个错误。
好吧,有几次我需要类似概念的功能,我转向Boost概念检查。 这不是最漂亮的图书馆,但它似乎有很多内置的东西。
我会使用你的方法的唯一问题是需要编写所有特征类。 我没有广泛使用它,但大概很多常见的东西已经用Boost完成了。
有一种C ++ 03方法可以完成概念提供的编译时检查的一部分。
检查是否提供了特定的成员
概念可以定义如下( if(0)
用于在链接期间抑制错误。 (void)test#
用于抑制未使用的变量警告。):
template <class T>
struct ForwardIterator {
ForwardIterator() {
if(0) {
void (T::* test1) () = &T::operator++; (void)test1;
}
}
};
template <class T>
struct BidirectionalIterator {
BidirectionalIterator() {
if(0) {
ForwardIterator<T> requirement_1;
void (T::* test1) () = &T::operator--; (void)test1;
}
}
};
并且可以在编译时使用模板实例化进行测试:
struct FooIterator {
void operator++() {}
};
template struct BidirectionalIterator<FooIterator>;
它具有给予编译错误的额外好处,(一旦你习惯了它们)比C ++ 11的static_assert提供的更好的可读性。 例如,gcc出现以下错误:
concept_test.cpp: In instantiation of ‘BidirectionalIterator<T>::BidirectionalIterator() [with T = FooIterator]’:
concept_test.cpp:24:17: required from here
concept_test.cpp:15:30: error: ‘operator--’ is not a member of ‘FooIterator’
void (T::* test1) () = &T::operator--; (void)test1;
^
即使MSVC2010也会生成一个有用的编译错误,其中包含模板参数T导致错误的值。 它不会为static_assert做这件事。
检查参数的类型和返回值
如果参数/返回类型取决于正在测试的类,那么被测试的类必须提供必要的typedef。 例如,以下概念测试一个类是否提供返回前向迭代器的开始和结束函数:
template <class T>
struct ForwardIterable {
ForwardIterable() {
if(0) {
ForwardIterator<typename T::Iterator> requirement_1;
typename T::Iterator (T::* test1) () = &T::begin; (void)test1;
typename T::Iterator (T::* test2) () = &T::end; (void)test2;
}
}
};
它的用法如下(注意typedef是必须的):
struct SomeCollection {
typedef FooIterator Iterator;
Iterator begin();
Iterator end();
};
template struct ForwardIterable<SomeCollection>;
签名检查
此方法还广泛检查签名。 在下面的代码中,编译器会检测到modifyFooItem
的参数modifyFooItem
应该是const。
struct SomeFoo;
template <class T>
struct TestBar {
TestBar() {
if(0) {
int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1;
}
}
};
struct SomeBar {
int modifyFooItem(const SomeFoo * item) {}
};
template struct TestBar<SomeBar>;
它会产生以下错误:
concept_test.cpp: In instantiation of ‘TestBar<T>::TestBar() [with T = SomeBar]’:
concept_test.cpp:61:17: required from here
concept_test.cpp:52:47: error: cannot convert ‘int (SomeBar::*)(const SomeFoo*)’ to ‘int (SomeBar::*)(SomeFoo*)’ in initialization
int (T::* test1) (SomeFoo * item) = &T::modifyFooItem; (void)test1;
检查概念的最好方法是使用替换失败。 但是,在C ++ 98中使用替换失败的检测相当有限。 在C ++ 11中,我们可以使用更强大的表达式来替代失败。 C ++ 11中的Tick库提供了定义概念谓词的简单方法。 例如,一个快速而肮脏的is_input_iterator
可以这样写:
TICK_TRAIT(is_input_iterator,
std::is_copy_constructible<_>)
{
template<class I>
auto requires_(I&& i) -> TICK_VALID(
*i,
++i,
i++,
*i++
);
};
然后,Tick还提供了一个TICK_REQUIRES
宏来添加模板约束( TICK_REQUIRES
处理所有的enable_if
样板文件),所以你可以像这样定义函数:
template<typename Iter, typename Val, TICK_REQUIRES(is_input_iterator<Iter>())>
Iter find(Iter b, Iter e, Val x)
{
// Rest of code...
}
理想情况下,你不想使用static_assert
因为它会产生编译错误。 所以我们无法检测到,如果find
,当用某些参数调用时, find
是有效的,因为它无效时会产生编译器错误。
上一篇: What to use as replacement for concepts (upcoming feature) in C++?