Structured Binding with [[Maybe_Unused]]

structured binding with [[maybe_unused]]

In the structure bindings paper:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0144r2.pdf

they discuss their reasoning:

3.8 Should there be a way to explicitly ignore components?

The motivation would be to silence compiler warnings about unused names.

We think the answer should be “not yet.” This is not motivated by use
cases (silencing compiler warnings is a motivation, but it is not a
use case per se), and is best left until we can revisit this in the
context of a more general pattern matching proposal where this should
fall out as a special case.

Symmetry with std::tie would suggest using
something like a std::ignore:

   tuple<T1,T2,T3> f(); 
auto [x, std::ignore, z] = f(); // NOT proposed: ignore second element

However, this feels awkward.

Anticipating pattern matching in the language
could suggest a wildcard like _ or *, but since we do not yet have
pattern matching it is premature to pick a syntax that we know will be
compatible. This is a pure extension that can wait to be considered
with pattern matching.

Although this does not explicitly address [[maybe_unused]], I assume the reasoning might be the same. Stopping compiler warnings is not a use-case.

structured bindings and range-based-for; supress unused warning in gcc

The relevant GCC pragmas are documented on this page.

#include <map>

std::map<int, int> my_map;

void do_something(int);

void loop()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
for (auto & [unused, val]: my_map)
#pragma GCC diagnostic pop
do_something(val);

}

This is the smallest scope of the disabled warning that I could have, and still have the warning suppressed with -Wall -Wextra -Werror.

[[maybe_unused]] with structured_binding?

As suggested by comments, -Wunused-variable only appears in gcc7

[[maybe_unused]] is not needed for newer gcc

std::ignore with structured bindings?

The structured bindings proposal contains a dedicated section answering your question (P0144R2):

3.8 Should there be a way to explicitly ignore components?

The motivation would be to silence compiler warnings about unused names.
We think the answer should be “not yet.” This is not motivated by use cases (silencing compiler warnings is a motivation, but it is not a use case per se), and is best left until we can revisit this in the context of a more general pattern matching proposal where this should fall out as a special case.

Symmetry with std::tie would suggest using something like a std::ignore:

tuple<T1,T2,T3> f();

auto [x, std::ignore, z] = f(); // NOT proposed: ignore second element

However, this feels awkward.

Anticipating pattern matching in the language could suggest a wildcard like _ or *, but since we do not yet have pattern matching it is premature to pick a syntax that we know will be compatible. This is a pure extension that can wait to be considered with pattern matching.

However, note that the working draft of the Standard is currently being revised by the relevant National Bodies (NB), and there is a NB comment requesting this feature (P0488R0, US100):

Decomposition declarations should provide syntax to discard some of the returned values, just as std::tie uses std::ignore.

Structured binding violations

So they both still violate the rule that that structured bindings are never names of variables, making them never capturable?

No, it is actually clang that is violating the standard, at least for the compiler flags provided.
In C++20, the restriction of not directly supporting captures of structured binding aliases has been lifted, allowing them to be directly used without falling back to constructs using init-captures:

Change [expr.prim.lambda.capture]p8 (7.5.5.2) as follows:

If a lambda-expression explicitly captures an entity that is not odr-usable or captures a structured binding (explicitly or implicitly)
, the program is ill-formed.

cv-qualifier propagation in structured binding

decltype has a special rule when the member of a class is named directly as an unparenthesized member access expression. Instead of producing the result it would usually if the expression was treated as an expression, it will result in the declared type of the member.

So decltype(rf.x) gives int, because x is declared as int. You can force decltype to behave as it would for other expressions by putting extra parentheses (decltype((rf.x))), in which case it will give const int& since it is an lvalue expression and an access through a const reference.

Similarly there are special rules for decltype if a structured binding is named directly (without parentheses), which is why you don't get const int& for decltype(x).

However the rules for structured bindings take the type from the member access expression as an expression if the member is not a reference type, which is why const is propagated. At least that is the case since the post-C++20 resolution of CWG issue 2312 which intends to make the const propagation work correctly with mutable members.

Before the resolution the type of the structured binding was actually specified to just be the declared type of the member with the cv-qualifiers of the structured binding declaration added, as you are quoting in your question.

I might be missing some detail on what declared type refers to exactly, but it seems to me that this didn't actually specify x to have type const int& in your first snippet (and decltype hence also not const), although that seems to be how all compilers always handled that case and is also the only behavior that makes sense. Maybe it was another defect, silently or unintentionally fixed by CWG 2312.

So, practically speaking, both rf.x and x in your example are const int lvalue expressions when you use them as expressions. The only oddity here is in how decltype behaves.

What part in cppreference tells me structured binding declarations only work with compile-time known objects?

The relevant part is std::tuple_size, which yields a compile-time size. You can't implement that for std::vector. It has a run-time variable std::vector::size.



Related Topics



Leave a reply



Submit