Подтвердить что ты не робот

В С++ 11 должно произойти первое: расширение строки или макросы?

Этот код работает в Visual С++ 2013, но не в gcc/clang:

#if 0
R"foo(
#else
int dostuff () { return 23; }
// )foo";
#endif
dostuff();

Visual С++ сначала удаляет if 0. Clang сначала расширяет строку R raw (и никогда не определяет dostuff). Кто прав и почему?

4b9b3361

Ответ 1

[Обновление: комментарии Адриана Маккарти ниже говорят, что MSVC++ 2017 исправляет это]

GCC и лязг правы, VC++ не правы.

2.2 Фазы перевода [lex.phases]:

[...]

  1. Исходный файл разлагается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии).

  2. Директивы предварительной обработки выполняются, [...]

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, он компилирует только файл.

Очевидно, что безопаснее всего не допустить, чтобы ваша необработанная строка пересекала границу препроцессора.