необъяснимое предупреждение при использовании std::apply для пустого кортежа с msvc

У меня есть предупреждение о переменной, на которую нет ссылки, при использовании std::apply для пустого кортежа. Этот фрагмент, вдохновленный std::apply cppreference, показывает проблему:

      #include <iostream>
#include <tuple>
#include <utility>

template <typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple) {
    std::apply(
        [&os](Ts const&... tupleArgs) {
            os << '[';
            std::size_t n{0};
            ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
            os << ']';
        },
        theTuple);
    return os;
}

int main() {
    // serialization example
    std::tuple myTuple{25, "Hello", 9.31f, 'c'};
    std::cout << myTuple << '\n';
    std::cout << std::tuple<>() << '\n';
}

Живой
MSVC, с/permissive-, выводит следующее предупреждение в самой последней строке:

(10): предупреждение C4189: 'n': локальная переменная инициализируется, но на нее нет ссылки
(22): примечание: см. ссылку на создание экземпляра шаблона функции 'std::ostream &operator (std::ostream &,const std::tuple &) ' компилируется

gcc и clangs ничего не выдают.

Поскольку я также используюWerror//weЯ хотел бы избавиться от этого предупреждения (по возможности, без использования прагмы).

  1. Почему msvc ведет себя так?
  2. Как мне избавиться от этого предупреждения (добавивif constexpr (sizeof...(Ts)>0)внутри лямбды?)?

1 ответ

Предлагаю ответ из комментариев.

Из выражения cppreference:

Пояснение...
При использовании унарной складки с раскрытием пачки нулевой длины разрешены только следующие операторы:

  1. Логическое И (&&). Значение для пустой упаковки истинно.
  2. Логическое ИЛИ (||). Значение пустого пакета неверно.
  3. Оператор запятая (,). Значение пустого пакета — void().

акцент мой. Таким образом, в((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);, если кортеж пуст, выражение принимает видvoid();, что приводит к тому, что он не используется, поэтому msvc имеет право выдать предупреждение.

Тогда это предупреждение можно заглушить простым[[maybe_unused]] std::size_t n{0};.

Возможно, мы могли бы также написать:

      #include <iostream>
#include <tuple>
#include <utility>

template <typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple) {
    std::apply(
        [&os](Ts const&... tupleArgs) {
            os << '[';
            if constexpr (sizeof...(Ts)) {
                std::size_t n{0};
                ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
            }
            os << ']';
        },
        theTuple);
    return os;
}

int main() {
    // serialization example
    std::tuple myTuple{25, "Hello", 9.31f, 'c'};
    std::cout << myTuple << '\n';
    std::cout << std::tuple<>() << '\n';
}

Жить

The if constexprявляется более подробным, но также и более явным в отношении того, когдаnфактически не используется.
Как ни странно, я не смог найти способ заставить gcc и clang выдавать подобное предупреждение.

Другие вопросы по тегам