Search code examples
c++structured-bindings

Structured Bindings Order and Evaluation


Sorry for the bad title to this question, but I really couldn't think of a better way to title it. My question involves structured bindings and maps, but couldn't really find anything in the standard that could give me a cast-iron guarantee. Consider the following:-

struct Obj {};

std::map<int, Obj> data;

if (const auto&[o, state] = data.insert(std::make_pair(0,  Obj())); state)
{
    // state is true if the object was inserted
}
else
{
    // Is it guaranteed that Obj() won't get called for a case where the key already exists?
    // Does the key get checked for its existence BEFORE the evaluation of the make_pair()?
}

At what point is the key evaluated for its existence, or is this a case of everything will be evaluated from the inside to the outside (so Obj() created, then make_pair(), then .insert attempted etc.


Solution

  • Does the key get checked for its existence BEFORE the evaluation of the make_pair()?

    No, that is not how C++ evaluates expressions. std::make_pair(0, Obj()) is fully evaluated, the pair is constructed before it is passed to insert method, or any other callable for that matter. So Obj always exists before the function is even called.

    Implementing this feature would require some form of lazy evaluation.

    But there is a solution exactly for this kind of problem - try_emplace:

    struct Obj {
       Obj(std::string, float, int);
    };
    int key=0;
    data.try_emplace(key, "Str", 0.2f, 5)
    

    This will first check if key exists, if it does, the arguments are not moved from and [iter,false] is returned. Otherwise Obj("Str",0.2f,5) is constructed in-place with perfect forwarding and returns [iter,true]. The post condition is that the returned iterator is always pointing to a pair equal to the parameters.

    Another upside of this solution is you no longer need to use the ugly std::piecewise_construct tag for ordinary emplace.