2014-10-03 116 views
4

我一直在网上浏览最近关于C++概念的详细信息,并且发现了几篇关于几篇论文被称为'run-time concepts'的参考文献。它们与编译时概念有什么不同?它们为什么首先被引入,它们将如何实现以及它们为什么对于C++的未来很重要?从浏览论文的角度来看,我认识到运行时概念旨在缓解目前存在于面向对象代码和泛型代码之间的当前紧张局势,但我没有从中得到更多的东西。什么是C++运行时概念?

+0

,如果他们无法解释它,这样它可以理解的,那么它是...不是重要;-) – 2014-10-03 19:23:38

+1

对我来说,他们的声音喜欢的人谁更喜欢在的ObjectiveC编程,但从来没有与该语言接触。相反,他们试图将更多的灵活性移植到C++中,而不会质疑对语言的普遍认可。仅仅因为这是他们所知道的语言。如果他们同时知道ObjectiveC和C++,他们就会简单地使用C++,这种方式与C++程序中常见的风格截然不同,而不是试图通过修正语言来解决这种风格的问题。因此,他们只贡献语言的功能臃肿...... – cmaster 2014-10-03 19:56:09

+0

@ Cheersandhth.-Alf:我不知道......这听起来对我来说很重要_pre_tty。 – RandomDSdevel 2014-10-03 19:58:33

回答

7

这是我对正在发生的事情的理解。它从另一个角度开始:类型擦除。

std::function<void()>是类型擦除类的一个例子。它采用“无参数调用,无返回”的概念,以及“复制构造”和“销毁”的帮助者概念,并将其包装成一个整洁的小包装。

所以,你可以做

void groot() { std::cout << "I am groot!\n"; } 
std::function<void()> f = groot; 
f(); 

groot被调用。或者,我们可以将lambda或函数对象或std::bind表达式或boost::function传递给std::function并调用它。

所有这些类型都可以被复制,销毁和调用:所以std::function可以使用它们并产生一个运行时接口。除了它们支持的操作之外,std::function可以存储和执行的类型是不相关的。没有将函数groot与lambda或boost::function关联的类层次结构。

std::function<void()>的构造函数需要的东西不是std::function s按照复制,销毁和调用void()的概念删除其参数。

我们先从这一点:

template<class Sig> 
struct func_type_eraser; 

template<class R, class... Args> 
struct func_type_eraser<R(Args...)> { 
    // invoke: 
    virtual R operator()(Args...) const = 0; 
    // copy: 
    virtual func_type_eraser* clone() const = 0; 
    // destroy: 
    virtual ~func_type_eraser() {}; 
}; 
template<class Sig, class T> 
struct func_type_eraser_impl; // TODO! 

在这里,我们的副本3个概念,破坏和调用,每一个表示为纯虚函数。

template<class Sig> 
struct function; 
template<class R, class... Args> 
struct function<R(Args...)> { 
    std::unique_ptr<func_type_eraser<R(Args...)>> pImpl; 
    // invoke: 
    R operator()(Args... args) const { 
    return (*pImpl)(std::forward<Args>(args)...); 
    } 
    // destroy: 
    ~function() = default; 
    // copy: 
    function(function const& o) : pImpl(o.pImpl ? o.pImpl->clone() : nullptr) {} 
    // move: 
    function(function&&) = default; 
    // TODO: operator= 

    // technical issues, ignore: 
    function(function& o) : function(const_cast<function const&>(o)) {} 
    function(function const&& o) : function(o) {} 

    // type erase: 
    template<class T> 
    function(T&& t) : pImpl(new func_type_eraser_impl<R(Args...), std::decay_t<T>>{std::forward<T>(t)}) 
    {} 
}; 

在这里,我们总结的概念了,我们要支持到什么是所谓的Regular类型 - 价值型类型。我们有一个潜在的指针和虚拟层次结构(一个小的,但尚未见过),但类型function看起来就像一个int - 您可以复制,分配等。

每个概念 - 调用,复制,移动,销毁 - 被转发到pImplmove除外,我们可以在此层有效实现)。

只有一半类型的擦除工作在这里完成。这部分让我们分配任何东西给我们的function类实例。通过测试,我们可以做得更好一点,T传递了概念要求 - 它可以被复制,销毁和调用所需的签名 - 在承认它给我们的构造函数之前。 (目前的C++ std::function没有做到这一点,非常烦恼)。

类型擦除的最后一部分是...:

template<class R, class... Args, class T> 
struct func_type_eraser_impl<R(Args...), T> : func_type_eraser<R(Args...)> { 
    // type erase storage: 
    T t; 
    // invoke: 
    virtual R operator()(Args... args) const override { 
    return t(std::forward<Args>(args)...); 
    } 
    // copy: 
    virtual func_type_eraser_impl* clone() const override { 
    return new func_type_eraser_impl{t}; 
    } 
    // destroy: 
    virtual ~func_type_eraser_impl() {} 
}; 

...我们实现func_type_eraser暴露特定类型T概念接口。

现在我们有4个概念,其中3个是擦除类型,一个是我们的常规类型包装器,我们可以存储任何支持这3个概念的东西。

我们可以更进一步:

我们甚至可以支持任何客户端可以提供的功能来支持这些概念。

这样做的最简单方法是在允许ADL(参数相关查找)的上下文中调用免费函数,如std::begin

我们的类型擦除实现不是直接与对象交互,而是调用ADL上下文中的自由函数。

提供该函数的默认实现,该函数执行从“失败”到“检查方法.begin()并调用它”或“做低效版本”或“检查传递类型的属性,并确定合理方式来完成任务“。

使用这种技术,我们可以允许客户扩展我们的类型擦除,并使用更广泛的概念。

作为一个具体的例子,假设我们有可打印的概念。如果它有ostream << X超载,或者它的print(X)超负荷,某物可打印。

我们将print_it添加到我们的类型擦除界面。它using impl_namespace::print,然后做一个print(t)

impl_namespace::print(X)只是做一个cout << X

这是全部解耦的。您可以采用别人写的没有打印概念的类型,通过其名称空间中的免费函数添加打印概念,然后将其传递到我们的类型擦除系统,类型擦除系统将其挂钩。

请参阅this channel 9 video了解使用类似技术构建玩具文档的示例,该文档具有无限撤消和显示功能,可以扩展到任意数量的类型(包括内置类型)。

现在,想象一下对此的语言支持。能够描述一组你想输入擦除的概念,并说“构建一个消除这些类型的常规类型”。

如果你有一个算法被其他概念所支持,你可以说“为这个算法提供类型擦除支持”。任何知道算法类型擦除的客户端和他们对它的更好支持都可以自动将自定义创建的客户端添加到您的界面中。那些不能使用你提供的类型擦除概念来实现它。

在擦除类型的地方,您的概念从编译时的虚拟和运行时间被理解,您的算法的类型擦除支持可以非常有效,即使支持您类型的概念是基于概念图的(即,提供用于解决问题的自定义函数,你的类型不是天真的可复制的,但是有一个克隆函数将它复制到合适的存储空间中)。算法概念类型擦除可以考虑完整编译时概念映射而不是运行时虚拟概念映射,即使没有根本上更快的算法,也可以提高性能。

如果极度谨慎,你可以拿更少的概念型擦除对象,如果新的概念是通过较少的概念,支持其扩展到一个有更多的概念。 “不知道”你想要快速二进制搜索(如)的客户最终会从它们的运行时界面支持它:那些会为你提供为你的类型定制的快速二进制搜索。

采取的另一个步骤,你可以在你的类型擦除类可选的概念支持。作为一个例子,类型擦除迭代器可以选择支持随机访问迭代。接受迭代器的算法可能会测试随机访问迭代,如果是这样,则创建更好的实现。二进制搜索范围的概念可能检查范围是否具有二分搜索概念支持,如果不具备随机访问支持,并且如果不具有随机访问支持,并且未能使用二进制搜索的前向迭代器版本(O(n)advance,O(lg( n))比较)。在每种情况下,它都可以使用“更专业化”的实施方式。

所有这些平行的概念在编译时是如何工作的。除了它在运行时发生,并且具有该额外类型的擦除系统。