有一种 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_asserts 执行此操作。
检查参数类型和返回值
如果参数/返回类型取决于被测试的类,则被测试的类必须提供必要的 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>;
签名检查
此方法还广泛检查签名。在下面的代码中,编译器会检测到参数 ofmodifyFooItem
不应该是 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;