dcmm88
dcmm88

Reputation: 1515

How to handle unused warnings caused by empty template parameter pack expansions?

An issue I keep facing is one where the compiler complains about an unused variable, even though the variable is used, but it's only used inside a parameter pack expansion that happens to be empty for a specific instantiation. For example:

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>());

See live example (try changing the tuple at line 4, by adding an int inside <> to see the warning go away). I know I could add a (void)var; line to make the warning go away, but it feels dirty to me, especially when the function is actually just a single line. I also don't want to disable this warning globally, because it does provide insight sometimes.

A similar manifestation of this issue is when the variable is used in a lambda capture. In this case, gcc spits no warning, while clang complains (I think gcc never implemented a warning about unused lambda captures):

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 example

Upvotes: 13

Views: 2212

Answers (5)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

(void)var; suppressed unused warnings in every compiler I have used:

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; has zero run time effects.

Upvotes: 2

Jarod42
Jarod42

Reputation: 217573

var is indeed not use with empty pack. Is it intended ? compiler can only guess.

Whereas clang consider than empty pack is a usage, gcc chooses the contrary.

You can silent the warning in different ways as:

  • attribute [[maybe_unused]] (C++17)
  • casting to void (static_cast<void>(arg))
  • or similar (template <typename T> void unused_var(T&&){} and then unused_var(var)).
  • creating overloads:

    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)...);
    }
    

    or in C++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)...);
        }
    }
    

Upvotes: 5

max66
max66

Reputation: 66210

Maybe there are other problems but... according the code you linked in compiler explorer, your var is a std::tuple<>; that is a std::tuple with zero components.

If I'm not wrong, std::get<Num>(std::tuple<Ts..>) is defined only when Num is in [0,sizeof...(Ts)); in this case in [0, 0), that is an empty interval.

I suppose that your code (when var is defined as std::tuple<>) is ill formed. So I suppose that the warning is correct (because there isn't cases when var is used) but doesn't warn about the real problem.

It's different when var is defined as std::tuple<int>: var is correctly used when all I are equal to zero, so var is (potentially) used and, as you observed, the warning disappears.

Upvotes: -1

Rakete1111
Rakete1111

Reputation: 48978

If you can use C++17, the [[maybe_unused]] attribute is the clearest solution IMO:

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

Upvotes: 7

Travis Gockel
Travis Gockel

Reputation: 27643

This seems to be a compiler bug in GCC. The easiest workaround is to mark var with [[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)...);
}

If you are force to use compilers that don't recognize [[gnu::unused]], you can fake use the variable with a 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)...);
}

Upvotes: 3

Related Questions