0%

Experience on C++ concepts

雖然Concept還沒正式列入C++ Standard中,不過提早經歷過總是好事..
Concept像Interface,定義一個靜態Concept該有些什麼東西
雖然可以用Interface完成,不過有時候我們不需要Runtime Polymorphism
常見的做法是用template來做

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>
template <typename T>
void print(T obj)
{
std::cout << obj.c_str() << ", length: " << obj.length() << "\n";
}
int main()
{
print(std::string("123"));
print(123); // Compile error
}

這方法最大的問題就是error message很難看懂, 常常跟Template打交道就有感覺了

1
2
3
4
5
6
7
a.cpp: In instantiation of ‘void print(T) [with T = int]’:
a.cpp:23:11: required from here
a.cpp:18:19: error: request for member ‘c_str’ in ‘obj’, which is of non-class type ‘int’
std::cout << obj.c_str() << ", length: " << obj.length() << "\n";
~~~~^~~~~
a.cpp:18:51: error: request for member ‘length’ in ‘obj’, which is of non-class type ‘int’
std::cout << obj.c_str() << ", length: " << obj.length() << "\n";

如果用Concept表達的話會是這個樣子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string>
template <class T>
concept bool Printable() {
return requires(const T& obj) {
{ obj.c_str() }-> const char *;
{ obj.length() } -> size_t;
};
}
void print(Printable obj)
{
printf("%d: %s\n", obj.length(), obj.c_str());
}
int main()
{
print(std::string("123"));
print(123);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
a.cpp: In function ‘int main()’:
a.cpp:17:11: error: cannot call function ‘void print(auto:1) [with auto:1 = int]’
print(123);
^
a.cpp:10:6: note: constraints not satisfied
void print(Printable obj)
^~~~~
a.cpp:4:14: note: within ‘template<class T> concept bool Printable() [with T = int]’
concept bool Printable() {
^~~~~~~~~
a.cpp:4:14: note: with ‘const int& obj’
a.cpp:4:14: note: the required expression ‘obj.c_str()’ would be ill-formed
a.cpp:4:14: note: the required expression ‘obj.length()’ would be ill-formed

面的Error告訴我們123不如的類型是int, 而int不符合 Printable這個Concept的要求

Limitiation

Concept只是Template的一層Wrapper而已,所以這樣的程式碼也是能通過的

1
2
3
4
5
6
7
8
9
10
11
12
13
emplate <class T>
concept bool Printable() {
return requires(const T& obj) {
{ obj.c_str() }-> const char *;
{ obj.length() } -> size_t;
};
}
void print(Printable obj)
{
if (!obj.empty()) {
std::cout << obj.c_str() << ", length: " << obj.length() << "\n";
}
}

明明Printable中沒定義empty的觀念,不過程式還是編譯通過,這個時候只能多加留意了