Можно ли сразу определить числовые литералы с помощью точки?

Начиная с С++ 11, было возможно создать пользовательские литералы. Как и ожидалось, можно вернуть сложные структуры из таких литералов. Однако при попытке использовать такие операторы, как 123_foo.bar():

struct foo {
    int n;
    int bar() const { return n; }
};

constexpr foo operator ""_foo(unsigned long long test)
{
    return foo{ static_cast<int>(test) };
}

int main() {
    return 123_foo.bar();
}

GCC и Clang отвергают это, говоря, что они не могут найти operator""_foo.bar. MSVC принимает его. Если я вместо этого напишу 123_foo.bar(), все три компилятора принимают его

Кто здесь? Действителен ли 123_foo.bar()?


Дополнительная информация:


Я склонен полагать, что это ошибка GCC и Clang . не является частью действительного идентификатора.

Ответ 1

TL;DR Clang и GCC верны, вы не можете написать a . сразу после определения пользователем целочисленного/плавающего литерала, это ошибка MSVC.

Когда программа компилируется, она идет по 9 фазам переводов по порядку. Ключевым моментом здесь является лексирование (разделение) исходного кода на токены, прежде чем принимать во внимание его семантический смысл.

На этой фазе действует максимальный munch, т. Е. Токены принимаются как самая длинная последовательность символов, которая синтаксически действительна. Например, x+++++y лексируется как x ++ ++ + y вместо x + ++ ++ y даже если первое не является семантически правильным.

Вопрос в том, что является самой длинной синтаксически допустимой последовательностью для 123_foo.bar. Следуя правилам производства для номера предварительной обработки, точная последовательность

pp-number → идентификатор pp-number-nondigit →... → идентификатор числа pp-nondigit³ →
pp-number nondigit³ → pp-number. nondigit³ →... → pp-number nondigit⁴. nondigit³ →
pp-number digit nondigit⁴. nondigit³ →... → pp-number digit² nondigit⁴. nondigit³ →
digit³ nondigit⁴. nondigit³

Что разрешает 123_foo.bar как видно из сообщения об ошибке