How to detect and process if an object is used as an l-value or as an r-value?

I have a container class called Properties . I want to add operator[](const std::string & name) , which will return a property with the specified name.

Now let's consider that there is no property with the specified name. In this case, I will not add a new Property with the specified name to my Properties if it is used as an l-value and throws an exception.

 Properties pts; pts.add("name1", val1); pts.add("name2", val2); pts["name1"] = val3; //OK pts["name3"] = val1; //OK creating new Property with value = val1 cout << pts["name4"]; //Ooops can't find Property with name = "name4", so throwing an exception 

Is this possible in C ++? How to write such an operator[] ?

+4
source share
2 answers

You can cover the cases you give, but not check if the conversion of the lvalue-to-rvalue value is actually happening. I do not think this is possible for a direct interception, so you need to provoke another transformation:

  • operator[] returns a proxy object, as John Zwink says. Just creating this proxy object does not create the key.

  • The proxy object has operator=(const V&) , so it processes the assignment by creating a key. If you wish, you can also have operator+= , operator++ , and the rest is not sure if you mean that any use of lvalue is okay or just a direct assignment.

  • The proxy object has a conversion to V& , which returns if the key does not already exist.

Edit: this seems to work dimly, although there are use cases that it does not cover, for example, passing the return value of operator[] function that takes V& and assigns it there. In addition, hiding the proxy + transform never leads to an exactly equivalent interface to what you would have with the original type, because implicit conversions can include at most one user transform, and the proxy โ€œusesโ€ this transform.

 #include <iostream> #include <map> #include <string> #include <stdexcept> struct FunnyMap; struct ProxyValue { FunnyMap *ptr; std::string key; ProxyValue(const std::string &key, FunnyMap *ptr) : ptr(ptr), key(key) {} operator int&(); int &operator=(int i); }; struct FunnyMap { std::map<std::string, int> values; ProxyValue operator[](const std::string &key) { return ProxyValue(key, this); } }; ProxyValue::operator int&() { if (ptr->values.count(key) != 0) { return ptr->values[key]; } else { throw std::runtime_error("no key"); } } int &ProxyValue::operator=(int i) { return ptr->values[key] = i; } void foo(int &i) { i = 4; } int main() { try { FunnyMap f; f["foo"] = 1; std::cout << f["foo"] << "\n"; std::cout << f["bar"]; // foo(f["bar"]); // also throws } catch (const std::exception &e) { std::cout << "Exception: " << e.what() << "\n"; } } 

Output:

 1 Exception: no key 
+4
source

You can return a proxy object to operator[] , and not just a reference to the contained value. Then you can set the flag in the proxy when it is assigned (that is, when the intermediary operator = is called). Then you can use the proxy destructor if it has never been assigned. Of course, you need to create a proxy server instance using boolean, indicating whether you want to require an assignment (no value exists) or not (the value is already set).

Note that it is generally considered improper practice to throw a destructor. In particular, the proxy destructor should not be thrown if it was called due to another exception (for example, an error between finding keys and assigning values). You will probably want to skip the throw from the proxy destructor if the exception is already in flight, and you can detect this condition with std::uncaught_exception() .

And finally, I will talk about this article about uncaught_exception() : http://www.gotw.ca/gotw/047.htm

It uses two arguments against using this function. First, it can sometimes return true when it is actually safe to throw. I affirm that we can live with this in your case, because we are trying to provide a security audit, and if we are sometimes not able to provide a security audit, then we are not much worse than before. And if we can agree that sometimes the check is not performed in order, then the second argument (โ€œmoralโ€) in the article can also be ignored (since your proxy will not have two different error handling mechanisms, it will have one that is usually effective but not always).

+4
source

All Articles