Концепции и порядок декларирования

Я экспериментировал с концепциями lite в GCC от SVN. Я столкнулся с проблемой, которая, как я подозреваю, связана с моим недостатком понимания, и я был бы признателен, если бы кто-нибудь мог указать мне в правильном направлении. Мой код:

#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&) до определения понятия, и я понятия не имею, почему.

Является ли это ожидаемым поведением с концепциями, или я делаю что-то неправильно?

Ответ 1

Проблема не имеет ничего общего с ADL), но только с поиском имен. Проект концепций, используемый GCC, - n4377, но стандартный проект С++, который я буду использовать, - 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 Требуемое выражение требует краткого выражать требования к аргументам шаблона. Требование - это требование, которое могут быть проверены путем поиска по имени (3.4) или путем проверки свойств типов и выражения.

[expr.prim.req]/6 Тело требования состоит из последовательности требования. Эти требования могут относиться к локальным параметрам, параметры шаблона и любые другие объявления, видимые из охватывающий контекст....

Имеет смысл. Мы знаем, что охватывающий контекст - это глобальное пространство имен, так что говорит n4140?

[basic.lookup.unqual]/1 Во всех случаях, перечисленных в 3.4.1, области ищут декларацию в порядке, указанном в каждом из соответствующие категории; поиск имени заканчивается, как только декларация найдено для имени. Если объявление не найдено, программа плохо сформирован.

Имя, используемое при определении функции, следующей за функциями declarator-id, который является членом пространства имен N (где, только для целей изложения, N может представлять глобальную область), должно быть объявленного до его использования в блоке, в котором он используется, или в одном из его закрывающие блоки (6.3) или, должны быть объявлены до его использования в namespace N...

Поскольку концепция относится к функции, применяется параграф выше.

Ответ 2

Так как вышеприведенные функции должны быть объявлены так, чтобы все функции, которые его используют, должны были знать об этом.

В классе функции объявляются в файле заголовка. Если они не являются участниками, они должны быть объявлены до их использования. Это связано с тем, что компилятор читает сверху вниз и только знает о функции, когда видит объявление.

Если вы замените код концепции и код рисования, он также должен работать.