The following code seems to always follow the true branch.
#include <map>
#include <iostream>
class TestClass {
// implementation
}
int main() {
std::map<int, TestClass*> TestMap;
if (TestMap[203] == nullptr) {
std::cout << "true";
} else {
std::cout << "false";
}
return 0;
}
Is it defined behaviour for an uninitialized pointer to point at nullptr
, or an artifact of my compiler?
If not, how can I ensure portability of the following code? Currently, I'm using similar logic to return the correct singleton instance for a log file
:
#include <string>
#include <map>
class Log {
public:
static Log* get_instance(std::string path);
protected:
Log(std::string path) : path(path), log(path) {};
std::string path;
std::ostream log;
private:
static std::map<std::string, Log*> instances;
};
std::map<std::string, Log*> Log::instances = std::map<std::string, Log*>();
Log* Log::get_instance(std::string path) {
if (instances[path] == nullptr) {
instances[path] = new Log(path);
}
return instances[path];
}
One solution would be to use something similar to this where you use a special function provide a default value when checking a map
. However, my understanding is that this would cause the complexity of the lookup to be O(n)
instead of O(1)
. This isn't too much of an issue in my scenario (there would only ever be a handful of logs), but a better solution would be somehow to force pointers of type Log*
to reference nullptr
by default thus making the lookup check O(1)
and portable at the same time. Is this possible and if so, how would I do it?
The map always value-initializes its members (in situations where they are not copy-initialized, of course), and value-initialization for builtin types means zero-initialization, therefore it is indeed defined behaviour. This is especially true for the value part of new keys generated when accessing elements with operator[]
which didn't exist before calling that.
Note however that an uninizialized pointer is not necessarily a null pointer; indeed, just reading its value already invokes undefined behaviour (and might case a segmentation fault on certain platforms under certain circumstances). The point is that pointers in maps are not uninitialized. So if you write for example
void foo()
{
TestClass* p;
// ...
}
p
will not be initialized to nullptr
.
Note however that you might want to check for presence instead, to avoid accumulating unnecessary entries. You'd check for presence using the find
member function:
map<int, TestClass*>::iterator it = TestMap.find(203);
if (it == map.end())
{
// there's no such element in the map
}
else
{
TestClass* p = it->second;
// ...
}
Yes, that's defined behaviour. If an element isn't yet in a map when you access it via operator[]
, it gets default constructed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With