本文展示了使用概念(concepts)来说明C++泛型编程设施和原理的编程技术。概念是C++表达泛型代码约束的方式。作为初始示例,本文提供了一个简单的类型系统,该系统消除了窄化转换并提供范围检查,而无需不必要的符号或运行时开销。概念被广泛用于提供用户定义的类型系统扩展。本文旨在展示概念的实用性和背后的基本思想,而不是提供C++泛型编程语言支持或标准库广泛支持的详细或完整解释。泛型编程是C++的组成部分,而不是一个孤立的子语言。最后,本文给出了概念设计关键部分的设计理念和起源,包括使用模式、与面向对象编程的关系、值参数、符号表示、概念类型匹配和定义检查。
本文的核心动机是展示如何使用C++20引入的概念(concepts)来:
论文遵循Alex Stepanov提出的泛型编程目标:"最通用、最高效、最灵活的概念表示",并满足以下设计要求:
Number<T>类型,消除危险的窄化转换Span类型,提供范围检查的数组访问概念是编译时求值的函数(谓词),可以接受类型参数:
template<typename T>
concept Num = integral<T> || floating_point<T>;
template<typename T, typename U>
concept Can_narrow_to =
! same_as<T, U> // 不同类型
&& Num<T> && Num<U> // 都是数值类型
&& (
(floating_point<T> && integral<U>) // 可能丢失小数部分
|| (numeric_limits<T>::digits > numeric_limits<U>::digits) // 可能截断
|| (signed_integral<T>!=signed_integral<U> && sizeof(T)==sizeof(U)) // 可能改变符号
);
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false;
// 只在可能窄化时进行运行时检查
T t = u;
return (t != u);
}
template<Num T>
class Number {
T val;
public:
template<Num U>
constexpr Number(const U u) : val{convert_to<T>(u)} { }
operator T() { return val; }
};
template<class T>
class Span {
T* p;
unsigned n;
public:
Span(Spanable auto& s) : p{data(s)}, n{size(s)} {}
T& operator[](Number<unsigned> i) {
return p[check(i)];
}
private:
unsigned check(unsigned nn) {
if (n <= nn) throw Span_range_error{};
return nn;
}
};
template<typename R, typename Pred = ranges::less>
concept Sortable_range =
ranges::random_access_range<R>
&& sortable<ranges::iterator_t<R>, Pred>;
template<typename R, typename Pred = ranges::less>
requires Sortable_range<R,Pred>
void sort(R& r, Pred p = {}) {
sort(r.begin(), r.end(), p);
}
概念通过使用模式来定义要求,而不是通过类似类定义的函数集合:
template<typename T, typename U = T>
concept equality_comparable = requires(T a, U b) {
{a==b} -> Boolean;
{a!=b} -> Boolean;
{b==a} -> Boolean;
{b!=a} -> Boolean;
}
这种方法的优势:
概念被设计为编译时函数,而不是类型的类型或类型的属性:
通过编译时检查和条件编译,实现零运行时开销:
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false; // 编译时确定,无运行时成本
// 只在必要时进行运行时检查
}
论文通过大量实际代码示例来验证方法的有效性:
Number<int> test() {
Number<unsigned int> ii = 0;
ii = 2; // OK
ii = -2; // throws - 捕获了符号错误
}
Span<int> sa{array};
int x = sa[10]; // OK if in range
int y = sa[-1]; // throws - 捕获了负索引错误
sort(vec); // 简洁的接口
sort(lst); // 编译时错误:list不支持随机访问
论文指出许多约束系统依赖于函数集合(类似类定义),而C++概念使用函数式方法,提供更大的灵活性。
// 理想的函数式语法(未采用)
void sort(Sortable_range& r);
// 实际采用的妥协语法
void sort(Sortable_range auto& r);
auto冗余论文包含了丰富的参考文献,涵盖了从C++语言设计到泛型编程理论的各个方面,主要包括:
总结:这是一篇具有重要理论和实践价值的论文,由C++语言创造者亲自撰写,全面展示了概念在C++泛型编程中的应用。论文不仅提供了实用的编程技术,还深入阐述了设计理念,对C++程序员和编程语言研究者都具有重要参考价值。