В С++ 11 должно произойти первое: расширение строки или макросы?
Этот код работает в Visual С++ 2013, но не в gcc/clang:
#if 0
R"foo(
#else
int dostuff () { return 23; }
// )foo";
#endif
dostuff();
Visual С++ сначала удаляет if 0. Clang сначала расширяет строку R raw (и никогда не определяет dostuff). Кто прав и почему?
Ответы
Ответ 1
[Обновление: комментарии Адриана Маккарти ниже говорят, что MSVC++ 2017 исправляет это]
GCC и лязг правы, VC++ не правы.
2.2 Фазы перевода [lex.phases]:
[...]
-
Исходный файл разлагается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии).
-
Директивы предварительной обработки выполняются, [...]
2.5. Предварительная обработка токенов [lex.pptoken] перечисляет string-literals
среди токенов.
Следовательно, синтаксический анализ должен tokenise строки буквального первой, "потребляя" в #else
и dostuff
определение функции.
Ответ 2
Я подумал, что стоит повторить интересную "причуду" лексической фазы. Содержимое внутри #if 0... #else
еще не игнорируется, как вы могли наивно представить (я был наивен, пока не проверил его). Вот два примера. Разница заключается лишь в дополнительном пробеле между R
и "
в объявлении необработанной строки, который находится внутри блока #if 0
.
#include <iostream>
using namespace std;
#if 0
const char* s = R"(
#else
int foo() { return 3; }
// )";
#endif
int main() {
std::cout << foo() << std::endl;
return 0;
}
Результаты в (gcc 6.3, С++ 14)
prog.cpp: In function ‘int main():
prog.cpp:12:19: error: ‘foo was not declared in this scope
std::cout << foo() << std::endl;
При добавлении пробела (в коде, который предположительно игнорируется компилятором!), Он может компилироваться:
#include <iostream>
using namespace std;
#if 0
const char* s = R "(
#else
int foo() { return 3; }
// )";
#endif
int main() {
std::cout << foo() << std::endl;
return 0;
}
Компилируется и работает с
3
Обратите внимание, что при использовании обычного неочищенного строкового литерала этой проблемы нет. Вам не разрешено разбивать строку не необработанного текста на новую строку, поэтому в этом случае строка необработанного текста игнорируется и не маркируется. Так что если вы избавились от R
, он компилирует только файл.
Очевидно, что безопаснее всего не допустить, чтобы ваша необработанная строка пересекала границу препроцессора.