Как обрабатывать неиспользуемые предупреждения, вызванные пустым расширением пакета шаблонов шаблонов?

Проблема, с которой я сталкиваюсь, - это то, где компилятор жалуется на неиспользуемую переменную, даже если эта переменная используется, но она используется только в расширении пакета параметров, которое бывает пустым для конкретного экземпляра. Например:

template <std::size_t... I>
auto func1(std::index_sequence<I...>)
{
  auto var = get_tuple();
  return func2(std::get<I>(var)...);
}

auto a = func1(std::make_index_sequence<0>());

См. живой пример (попробуйте изменить кортеж в строке 4, добавив int внутри < > , чтобы увидеть предупреждение). Я знаю, что я мог бы добавить строку (void)var;, чтобы предупреждение исчезло, но мне кажется, что это грязно, особенно когда функция на самом деле представляет собой только одну строку. Я также не хочу отключать это предупреждение во всем мире, потому что он иногда дает представление.

Аналогичное проявление этой проблемы заключается в том, когда переменная используется в лямбда-захвате. В этом случае gcc не дает никакого предупреждения, в то время как clang жалуется (я думаю, gcc никогда не реализовал предупреждение о неиспользуемых лямбда-захватах):

template <std::size_t... I>
auto func1(std::index_sequence<I...>)
{
  auto var = get_tuple();
  auto my_lambda = [var](){
    return func2(std::get<I>(var)...);
  };
  return my_lambda();
}

auto a = func1(std::make_index_sequence<0>());

пример clang

Ответы

Ответ 1

Если вы можете использовать С++ 17, атрибут [[maybe_unused]] является самым ясным решением IMO:

[[maybe_unused]]
auto tuple = get_tuple();

Ответ 2

var действительно не используется с пустым пакетом. Это предназначено? компилятор может только догадываться.

В то время как clang считает, что пустой пакет является использованием, gcc выбирает противоположное.

Вы можете отключить предупреждение по-разному:

  • [[maybe_unused]] (С++ 17)
  • литье в void (static_cast<void>(arg))
  • или аналогичный (template <typename T> void unused_var(T&&){}, а затем unused_var(var)).
  • создание перегрузок:

    auto func1(std::index_sequence<>)
    {
      return func2();
    }
    
    template <std::size_t... I>
    auto func1(std::index_sequence<I...>)
    {
      auto var = get_tuple();
      return func2(std::get<I>(var)...);
    }
    

    или в С++ 17

    template <std::size_t... I>
    auto func1(std::index_sequence<I...>)
    {
        if constexpr (sizeof ...(I) == 0) {
            return func2();
        } else {
            auto var = get_tuple();
            return func2(std::get<I>(var)...);
        }
    }
    

Ответ 3

Это похоже на ошибку компилятора в GCC. Самое легкое обходное решение - отметить var с помощью [[gnu::unused]]:

template <std::size_t... I>
auto func1(std::index_sequence<I...>)
{
  auto var [[gnu::unused]] = get_tuple();
  return func2(std::get<I>(var)...);
}

Если вы вынуждены использовать компиляторы, которые не распознают [[gnu::unused]], вы можете подделать эту переменную с помощью static_cast<void>:

template <std::size_t... I>
auto func1(std::index_sequence<I...>)
{
  auto var = get_tuple();
  static_cast<void>(var);
  return func2(std::get<I>(var)...);
}

Ответ 4

(void)var; подавлено неиспользованные предупреждения в каждом компиляторе, который я использовал:

template <std::size_t... I>
auto func1(std::index_sequence<I...>)
{
  auto var = get_tuple();
  (void)var;
  return func2(std::get<I>(var)...);
}
auto a = func1(std::make_index_sequence<0>());

(void)variable; имеет нулевые эффекты времени выполнения.

Ответ 5

Возможно, есть другие проблемы, но... согласно коду, который вы связали в проводнике компилятора, ваш var является std::tuple<>; то есть std::tuple с нулевыми компонентами.

Если я не ошибаюсь, std::get<Num>(std::tuple<Ts..>) определяется только тогда, когда Num находится в [0,sizeof...(Ts)); в этом случае в [0, 0), то есть пустой интервал.

Я полагаю, что ваш код (когда var определяется как std::tuple<>) плохо сформирован. Поэтому я полагаю, что предупреждение правильное (потому что не существует случаев, когда используется var), но не предупреждает о реальной проблеме.

Он отличается, когда var определяется как std::tuple<int>: var корректно используется, когда все I равны нулю, поэтому var используется (потенциально) и, как вы заметили, предупреждение исчезает.