aldr
aldr

Reputation: 848

if-with-initializer in structured binding declaration example ill formed?

I was reading through the Structured binding declaration on cppreference.com

I thought I was doing well understanding the examples until I reached the last example at the bottom.

#include <set>
#include <string>
#include <iomanip>
#include <iostream>

int main() {
    std::set<std::string> myset;
    
    if (auto [iter, success] = myset.insert("Hello"); success) 
        std::cout << "insert is successful. The value is " << 
        std::quoted(*iter) << '\n';
    else
        std::cout << "The value " << std::quoted(*iter) << " already 
        exists in the set\n";
}

At first glance it looked all right, but the more I looked at it the less I understood the if clause.

At the top are the possible forms listed as:

attr(optional) cv-auto ref-operator(optional) [ identifier-list ] = expression ; (1)

attr(optional) cv-auto ref-operator(optional) [ identifier-list ] { expression } ; (2)

attr(optional) cv-auto ref-operator(optional) [ identifier-list ] ( expression ) ; (3)

To me it looks like to be of deduction type (1). But it kind of makes no sense to me cause of

expression - an expression that does not have the comma operator at the top level (grammatically, an assignment-expression), and has either array or non-union class type. If expression refers to any of the names from identifier-list, the declaration is ill-formed.

which says that the declaration is ill formed if the expression is in the identifier list. So this looks to me that success isn't part of expression. If this would be the case then

auto [iter, success] = myset.insert("Hello");

would only assign the inserted "Hello" to iter and success would be what?!? The other way around it would violate the expression part. But it clearly compiles and runs so I must be missing something

Upvotes: 0

Views: 303

Answers (2)

SergeyA
SergeyA

Reputation: 62613

I feel like you are getting confused by structured binding and if with initializer, but the simple way to look at it is to decompose them.

if (auto [a, b] = foo(); b)

Consists of two parts = first is initializer (auto [a, b] = foo()) and second (b) is condition. It is exactly equivalent to

{
auto [a, b] = foo(); 
if (b)
}

In the example above, a and b would be deduced to whatever return type of foo mandates, having nothing to do with if condition below that.

Upvotes: 1

NathanOliver
NathanOliver

Reputation: 181068

which says that the declaration is ill formed if the expression is in the identifier list

It doesn't say that. It says that if expression uses any of the names in the identifier-list it is ill-formed. myset.insert("Hello") doesn't do that so it is okay.

std::set::insert returns a std::pair<std::set::iterator, bool> where the std::set::iterator marks the elements position and the bool indicates if it was inserted or not. So auto [iter, success] = myset.insert("Hello") captures that pair and sets iter to the std::set::iterator and success to the bool.

Upvotes: 5

Related Questions