Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concepts and declaration order

I've been experimenting with concepts lite in GCC from SVN. I've hit upon a problem which I suspect is due to my lack of understanding, and I'd appreciate it if anyone could point me in the right direction. My code is:

#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
}

Here I define a simple concept, Drawable, which is intended to require that a given a parameter of type const T&, the function draw(t) compiles.

I then define a function draw(const std::string&) which "draws" the string to cout. Finally, I check whether std::string matches the Drawable concept -- which I would have expected it does, since an appropriate draw() function is in scope when the static_assert is called.

However, the static assert fails, unless I include a declaration of draw(const std::string&) before the concept definition, and I have no idea why.

Is this expected behaviour with concepts, or am I doing something wrong?

like image 883
Tristan Brindle Avatar asked Dec 15 '15 00:12

Tristan Brindle


1 Answers

The problem has nothing to do with ADL) but solely with name lookup. The concepts draft used by GCC is n4377 but the C++ standard draft I will be using is n4140. First, before diving into standardese we can turn your problem into an MCVE of a form we know is supposed to work. Example:

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

This is a simple requirement, [expr.prim.req.simple], that checks the validity of the expression. Rewriting our example to match the form:

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

We can see our syntax is fine. OK, what does n4377 say?

[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. ...

Makes sense. We know the enclosing context is the global namespace, so what does n4140 say?

[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 ...

As the concept appertains to the function, the paragraph above applies.

like image 152
uh oh somebody needs a pupper Avatar answered Oct 14 '22 15:10

uh oh somebody needs a pupper