Сложить выражения в MSVC

У меня есть следующая функция, которая вычисляет среднее значение:

template<typename... Ts>
auto mean_of(const Ts... values)
{
    return (... + values) / static_cast<double>(sizeof...(Ts));
}

С VS 2017 15.6.0 Preview 3 следующий код

std::cout << mean_of(1, 3);

выводит 2.5. Кажется, что MSVC интерпретирует выражение fold как 1 + 3 / N, а не как (1 + 3) / N. Если я добавлю дополнительные скобки вокруг выражения fold, результат будет правильным. С помощью GCC дополнительных скобок не требуется.

Является ли это ошибкой в ​​MSVC или нужны дополнительные скобки?

Ответ 1

Интересный вопрос.

Исправляя мою первую интерпретацию, мне кажется, что g++ и clang++ правы и что MSVC ошибочен.

Я предполагаю это, потому что в проекте n4659 для С++ 17 (извините: у меня нет доступа к окончательной версии). Я вижу правила выражения (A.4), в которых оператор деления участвует в "мультипликативном -expression" следующим образом

мультипликативное выражение/pm-выражение

"Мультипликативное выражение" может быть также "pm-выражением", которое может быть "выражением-выражением", которое может быть "унарным выражением", которое может быть "постфиксным выражением", которое может быть "первичное выражение", которое может быть "сглаживанием"

Таким образом, правило можно увидеть как

fold-expression/pm-expression

Итак, если я не ошибаюсь, "fold-expression" следует оценивать как целое до применения деления.

Моя первая интерпретация (MSVC right, g++ и clang++ неправильная) была основана на поспешной лекции 17.5.3

В результате создания выражения сложения получается:

(9.1) ((E1 op E2) op ···) op EN для унарной левой складки

и 8.1.6

Выражение в форме (... op e), где op - оператор сгиба, называется унарной левой складкой.

Поэтому я предположил, что

return (... + values) / static_cast<double>(sizeof...(Ts));

должен быть создан

return ((v1 + v2) + ... ) + vn / static_cast<double>(sizeof...(Ts));

В любом случае... правый MSVC или нет... чтобы быть уверенным, что вы хотите

return (1 + 3) / 2.0;

Я предлагаю вам добавить еще пару круглых скобок.

Ответ 2

Это ошибка в MSVC. Я уменьшил его до:

template<class... Ts>
constexpr auto f1(Ts const... vals) {
    return 0 * (vals + ...);
}

template<class... Ts>
constexpr auto f2(Ts const... vals) {
    return (vals + ...) * 0;
}

static_assert(f1(1,2,3) == 0);
static_assert(f1(1,2,3) != 0 * 1 + (2 + 3));
static_assert(f2(1,2,3) == 0);
static_assert(f2(1,2,3) != 1 + (2 + 3) * 0);

(который компилируется отлично с GCC и clang, но запускает все четыре static_assert в MSVC) и хранит его внутри.

20180205 Обновление: эта ошибка исправлена ​​для будущей версии Visual С++.