概念和声明顺序

时间:2015-12-15 00:08:16

标签: c++ c++17 c++-concepts

我一直在尝试使用SVN的GCC中的概念精简版。我遇到了一个问题,我怀疑是因为我缺乏理解,如果有人能指出我正确的方向,我会很感激。我的代码是:

#include <iostream>
#include <string>

// Uncomment this declaration to change behaviour
//void draw(const std::string&);

template <typename T>
concept bool Drawable() {
    return requires (const T& t) {
        { draw(t) }
    };
}

void draw(const std::string& s)
{
    std::cout << s << "\n";
}

int main()
{
    static_assert(Drawable<std::string>()); // Fails
}

这里我定义了一个简单的概念Drawable,它旨在要求给定一个const T&类型的参数,函数draw(t)编译。

然后我定义了一个函数draw(const std::string&),它将字符串“绘制”到cout。最后,我检查std::string是否与Drawable概念匹配 - 这是我预期的概念,因为调用draw()时适当的static_assert函数在范围内。

然而,静态断言失败,除非我在概念定义之前包含draw(const std::string&) 的声明,并且我不知道为什么。

这是概念的预期行为,还是我做错了什么?

2 个答案:

答案 0 :(得分:1)

问题与ADL无关),但仅与名称查找有关。 GCC使用的概念草案是n4377,但我将使用的C ++标准草案是n4140。首先,在深入了解标准之前,我们可以将您的问题转变为我们知道假设的形式的MCVE。例如:

template<typename T> concept bool C =
  requires (T a, T b) {
    a + b;
  };

这是一个简单的要求,[expr.prim.req.simple],它检查表达式的有效性。重写我们的示例以匹配表单:

template<typename T> concept bool Drawable = 
  requires (const T& x) { 
    draw(x); 
  };

我们可以看到我们的语法很好。好吧,n4377说什么?

  

[expr.prim.req] / 1 requires-expression 提供了一种简洁的方法   表达对模板参数的要求。要求是一个要求   可以通过名称查找(3.4)或通过检查类型的属性来检查   和表达。

     

[expr.prim.req] / 6需求主体由一系列序列组成   要求。这些要求可能涉及本地参数,   模板参数,以及从中可见的任何其他声明   封闭的背景。 ...

有道理。我们知道封闭上下文是全局命名空间,所以n4140说的是什么?

  

[basic.lookup.unqual] / 1在3.4.1中列出的所有情况下,范围   按照每个中列出的顺序搜索声明   各自的类别;名称查找在声明后立即结束   找到了这个名字。如果没有找到声明,程序就是   不良形成。

     

函数定义后使用的名称    declarator-id ,它是命名空间N的成员(其中,仅出于展示的目的,N可以表示全局范围)应为   在使用它之前或在其中一个中使用之前声明   其封闭块(6.3)或应在其使用前声明   名称空间N ...

由于概念属于该功能,上述段落适用。

答案 1 :(得分:-1)

因为,需要声明上面的函数,以便使用它的所有函数都需要知道它。

在类中,函数在头文件中声明。如果他们不是会员,那么他们需要在使用之前申报。这是因为编译器从上到下读取,只有在看到声明时才知道函数。

如果你交换概念代码和绘图代码,它也应该有效。