Бьярн ошибается насчет этого примера ADL, или у меня есть ошибка компилятора?

81

Я читаю язык программирования C ++, 4-е издание ( Бьярна Страуструпа ) о. Вот цитата (26.3.6, Сверхагрессивный ADL):

Поиск, зависящий от аргументов (часто называемый ADL), очень полезен, чтобы избежать многословия (14.2.4). Например:

#include <iostream>

int main()
{
    std::cout << "Hello, world" << endl; // OK because of ADL
}

Без поиска, зависящего от аргументов, endlманипулятор не будет найден. Как это, замечает , компилятор , что первый аргумент <<приведен ostreamопределенный в std. Таким образом, он ищет endlв stdи находит его (в <iostream>).

И вот результат компилятора (режим C ++ 11):

prog.cpp: In function ‘int main()’:
prog.cpp:4:36: error: ‘endl’ was not declared in this scope
 std::cout << "Hello, world" << endl;
                                ^

Либо это ошибка в компиляторе, либо в книге. Что говорит стандарт?

Обновить:

Мне нужно немного уточнить. Я знаю, что правильный ответ - использовать std::endl. Вопрос был о тексте в книге. Как уже сказал Лахлан Истон , это не просто опечатка. Весь абзац (вероятно) неверен. Я могу согласиться с такой ошибкой, если книга написана другим (менее известным) автором, но я был (и все еще сомневаюсь), потому что она была написана Бьярном.

маверик
источник
12
std::endlнет ошибки
aaronman 06
3
По моему опыту, книги печально известны ошибками и опечатками. Надеюсь, в хорошей книге лишь незначительные / очевидные.
Нил Кирк
31
@aaronman OP, очевидно, знает об этом. Из цитаты видно, что Бьярн (создатель C ++) утверждает, что std::в этом случае не требуется из-за ADL. Но это не компилируется, отсюда и вопрос.
BlueRaja - Дэнни Пфлугхофт,
6
Да, дело в том, что книга явно не то говорит. Это не опечатка, целый абзац был написан, чтобы описать то, что на самом деле не соответствует действительности. Это ошибка в книге.
DanielKO
7
@maverik Это является ошибка в книге. Я сообщил ему об этой проблеме пару минут назад, дам знать его ответ.
Али

Ответы:

83

Это не ошибка компилятора. ADL используется для поиска функций, а не аргументов . operator<<- это функция, которую можно найти здесь через ADL, просмотрев параметры std::coutи (что должно быть) std::endl.

Питер Александр
источник
2
Фактически, в ретроспективе это вдохновляет на способ сделать код действительным, используя тот факт, что std::endlна самом деле (и сбивает с толку) функцию:endl(std::cout << "Hello, world"); // OK because of ADL
alfC
49

Для тех, кто говорит, что это опечатка, это не так. Либо Бьярн ошибся, либо компилятор ошибся. Абзац после того, что опубликовал OP, гласит

Без поиска, зависящего от аргументов, манипулятор endl не был бы найден. Как бы то ни было, компилятор замечает, что первый аргумент << - это поток, определенный в std. Следовательно, он ищет endl в std и находит его (in <iostream>).

Лахлан Истон
источник
18
Вы, сэр, похоже, единственный человек здесь, который действительно прочитал это в книге. Это либо значительное изменение в правилах языка, что делает все текущие компиляторы C ++ нестандартными (для C ++ 11), либо вопиющая ошибка г-на Страуструпа (а не просто опечатка). Я бы предпочел подождать два дополнительных месяца, чтобы получить исправленное издание. Ему лучше снова отрастить бороду.
DanielKO
Кстати, разметка съела последний бит в цитате, вы, вероятно, захотите использовать обратные кавычки, «(in <iostream>)».
DanielKO
20

Это опечатка в книге, как уже указывали другие. Однако в книге имеется в виду, что нам придется написать

std::operator<<(std::cout, "Hello, world").operator<<(std::endl);

без ADL. Вот что Бьярн имел в виду под многословием.


Я исправился. Как отмечает Лахлан Истон , это не опечатка, а ошибка в книге. У меня нет доступа к этой книге, поэтому я не мог прочитать этот абзац и понять это сам. Я сообщил об этой ошибке Бьярну, чтобы он исправил ее.


Смешной. Тот же пример есть в Википедии и

Обратите внимание, что std::endlэто функция, но для нее требуется полная квалификация, поскольку она используется в качестве аргумента для operator<<( std::endlявляется указателем на функцию, а не ее вызовом).

Несомненно, это ошибка в книге. Тем не менее пример std::operator<<(std::cout, "Hello, world").operator<<(std::endl);показывает, как ADL помогает уменьшить многословие.


Спасибо gx_ за указание на мою ошибку .

Али
источник
Это было больше, чем опечатка, он что-то придумал (как происходит поиск std::operator<<) и написал целый абзац с неверной информацией. Это действительно заставляет вас поверить, что правила ADL изменились и что компиляторы теперь не работают.
DanielKO
на самом деле кажется, что в книге довольно много опечаток, например, 17.2.5
AndersK 07
@DanielKO Я поправляюсь; Я исправил свой ответ, спасибо. У меня нет доступа к этой книге, поэтому я подумал, что это опечатка. В любом случае ADL действительно помогает уменьшить многословие, и приведенный мной код является примером этого. В любом случае спасибо, что сказали мне.
Али
На самом деле то, что мы должны были бы написать, действительно std::operator<<(std::cout, "Hello, world").operator<<(std::endl);(см. Не-членoperator<< и членoperator<< )
gx_
10

Подсказка содержится в названии «поиск, зависящий от аргументов».

Это поиск неквалифицированных имен функций, который работает в зависимости от аргументов .

Это не имеет ничего общего с Lookup для аргументов.

Бьярн оговорился.

Гонки легкости на орбите
источник
8

У меня нет книги, но похоже, что это ошибка в книге. Отсутствие квалификатора пространства имен не имеет ничего общего с ADL. Так и должно быть std::endl.

Borgleader
источник
1
Я согласен. Но это довольно странное заявление (я имею в виду то, что написано в книге). Надеюсь, Бьярн должен об этом знать.
maverik
@maverik Может быть, он уже это делает, я не удивлюсь, если кто-то уже сообщил об этом. Если нет, то вы могли бы :)
Borgleader 06
@maverik, это просто опечатка, я думаю, кто-то еще это заметил
aaronman
2
Да, действительно, я неправильно понял все утверждения, которые std::coutон говорил о поиске operator<<, а не endl.
maverik
4

Да, это ошибка - пример неверно сформирован и не должен компилироваться. ADL применяется к неквалифицированным именам функций, которые представляют выражения вызова функций. endlИдентификационное выражение, пытающееся выполнить поиск std::endl. endlне вводит выражение вызова функции, поэтому для него не используется поиск, зависящий от аргументов, используется только неквалифицированный поиск, поэтому он не найдетstd::endl должным образом.

Более простой и правильный пример:

#include <vector>

int main()
{
    std::vector<int> x, y;
    swap(x,y); // calls std::swap due to ADL
}

Таким образом, перед поиском вызова функции (например f(x,y,z)) с неквалифицированным идентификатором (например f), сначала параметры функции (например,x,y,z анализируются ), чтобы определить их тип. Список связанных пространств имен формируется на основе типов (например, включающее пространство имен определения типа является связанным пространством имен). Затем в этих пространствах имен выполняется дополнительный поиск функции.

Целью примера Бьярна является демонстрация ADL std::operator<<функции, а не std::endl. Это требует дополнительного понимания того, что перегруженные операторы на самом деле являются выражениями вызова функций, x << yзначит operator<<(x,y), и operator<<является неквалифицированным именем, и поэтому к нему применяется ADL. Тип LHS это std::ostreamтак , stdэто Связанное пространство, и , следовательно , std::operator<<(ostream&, ...)будет найден.

Исправленный комментарий должен гласить:

Без поиска, зависящего от аргументов, перегруженный <<оператор в stdпространстве имен не будет найден. Как бы то ни было, компилятор замечает, что первый аргумент << - это поток, определенный в std. Поэтому он ищет оператор <<в std и находит его (in <iostream>).

Эндрю Томазос
источник