C ++概念与static_assert

时间:2014-04-15 21:58:33

标签: c++ static-assert c++-concepts

c ++概念中有什么新东西?在我的理解中,它们在功能上等同于使用static_assert,但是以“漂亮”的方式意味着编译器错误将更具可读性(因为Bjarne Stroustup说你不会得到10页或错误,而只是一个)。

基本上,您可以使用static_assert实现的所有概念都可以实现吗?

我有什么遗失的吗?

1 个答案:

答案 0 :(得分:19)

TL;博士

static_assert相比,概念更强大,因为:

  • 它们为您提供了良好的诊断,使用static_asserts
  • 无法轻松实现
  • 他们可以让您轻松地使模板功能超载std::enable_if(仅static_asserts不可能)
  • 他们允许您定义静态接口并重用它们而不会丢失诊断(每个函数中需要多个static_asserts
  • 他们让您更好地表达您的意图并提高可读性(这是模板的一个大问题)

这可以缓解世界:

  • 模板
  • 静态多态性
  • 重载

并成为有趣范例的构建模块。


什么是概念?

概念表达满足特定要求的类型的“类”(不是在C ++术语中,而是作为“组”)。作为一个例子,您可以看到Swappable概念表达了以下类型的集合:

  • 允许拨打std::swap

您可以很容易地看到,例如,std::stringstd::vectorstd::dequeint等......满足此要求,因此可以互换使用像这样的函数:

template<typename Swappable>
void func(const Swappable& a, const Swappable& b) {
    std::swap(a, b);
}

概念always existed in C++,将在(可能接近)未来添加的实际功能将允许您用该语言表达和强制执行它们。


更好的诊断

就更好的诊断而言,我们现在必须相信委员会。但他们“保证”的输出:

error: no matching function for call to 'sort(list<int>&)'
sort(l); 
      ^
note: template constraints not satisfied because 
note: `T' is not a/an `Sortable' type [with T = list<int>] since
note: `declval<T>()[n]' is not valid syntax

非常有前途。

确实,你可以使用static_assert来获得类似的输出,但是每个函数需要不同的static_assert s,这可能会非常繁琐。

作为一个例子,假设您必须在采用模板参数的2个函数中强制执行Container概念给出的要求量;你需要在两个函数中复制它们:

template<typename C>
void func_a(...) {
    static_assert(...);
    static_assert(...);
    // ...
}

template<typename C>
void func_b(...) {
    static_assert(...);
    static_assert(...);
    // ...
}

否则你将失去区分哪个要求不满足的能力。

使用概念,您可以通过简单地编写概念来定义概念并强制执行:

template<Container C>
void func_a(...);

template<Container C>
void func_b(...);

概念重载

引入的另一个重要功能是能够在模板约束上重载模板函数。是的,std::enable_if也可以这样做,但我们都知道会变得多么丑陋。

作为一个示例,您可以使用一个适用于Container的函数,并使用恰好与SequenceContainer s更好地运行的版本重载它:

template<Container C>
int func(C& c);

template<SequenceContainer C>
int func(C& c);

没有概念的替代方案是:

template<typename T>
std::enable_if<
    Container<T>::value,
    int
> func(T& c);

template<typename T>
std::enable_if<
    SequenceContainer<T>::value,
    int
> func(T& c);

绝对丑陋,可能更容易出错。


更清晰的语法

正如您在上面的示例中所看到的,语法对于概念来说绝对更清晰,更直观。这可以减少表达约束所需的代码量,并且可以提高可读性。

如前所述,您可以通过以下方式实现可接受的水平:

static_assert(Concept<T>::value);

但是那时你将失去对不同static_assert的强大诊断。使用概念,您不需要这种权衡。


静态多态性

最后,概念与其他功能范例(如Haskell中的类型类)有着相似之处。例如,它们可用于定义静态接口

例如,让我们考虑(臭名昭着的)游戏对象界面的经典方法:

struct Object {
    // …
    virtual update() = 0;
    virtual draw() = 0;
    virtual ~Object();
};

然后,假设你有一个派生对象的多态std::vector,你可以这样做:

for (auto& o : objects) { 
    o.update();
    o.draw();
}

很好,但除非您想使用多继承或基于实体组件的系统,否则每个类只有一个可能的接口。

但是如果你真的想要静态多态(多态不是那么动态)你可以定义一个Object概念,需要update }和draw成员函数(可能还有其他函数)。

此时你可以创建一个免费功能:

template<Object O>
void process(O& o) {
    o.update();
    o.draw();
}

之后,您可以为游戏对象定义另一个具有其他要求的界面。这种方法的优点在于您可以根据需要开发任意数量的接口而无需

  • 修改课程
  • 需要基类

它们都在编译时检查并强制执行。

这只是一个愚蠢的例子(而且非常简单),但概念确实为C ++中的模板开辟了一个全新的世界。

如果你想要更多的信息,你可以{C}概念与Haskell类型类read this nice article