概念及申报顺序

Concepts and declaration order

我一直在尝试使用来自 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&) 将 "draws" 的字符串 cout。最后,我检查 std::string 是否与 Drawable 概念相匹配——这是我所期望的,因为在调用 static_assert 时适当的 draw() 函数在范围内。

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

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

因为需要声明上述函数,以便所有使用它的函数都需要了解它。

在class中,函数是在头文件中声明的。如果他们不是成员,则需要在使用之前声明它们。这是因为编译器从上到下读取,只有在看到声明时才知道函数。

如果您交换概念代码和绘图代码,它应该也可以工作。

问题与 ADL 无关,而仅与名称查找有关。 GCC 使用的概念草案是 n4377,但我将使用的 C++ 标准草案是 n4140。首先,在深入研究标准语之前,我们可以将您的问题转化为我们知道 supposed 可以工作的形式的 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 A requires-expression provides a concise way to express requirements on template arguments. A requirement is one that can be checked by name lookup (3.4) or by checking properties of types and expressions.

[expr.prim.req]/6 The requirement-body is comprised of a sequence of requirements. These requirements may refer to local parameters, template parameters, and any other declarations visible from the enclosing context. ...

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

[basic.lookup.unqual]/1 In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.

A name used in the definition of a function following the function’s declarator-id that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N ...

由于概念属于函数,因此适用上述段落。