Reputation: 3035
I have a std::map<int, std::vector<SomeStruct>>
,
and provide a query like std::vector<SomeStruct> FindData(int key)
.
To prevent copying the whole data, I modify it to be std::vector<SomeStruct>& FindData(int key)
.
But, there will be no data for certain key
, so sometimes I have nothing to return.
In that case, I declare a file scope variable that is an empty std::vector<SomeStruct>
and return it.
But if I choose the pointer to vector, that is std::vector<SomeStruct>* FindData(int key)
then I can just return NULL
for non-existing key
.
Which one is better?
I learned that pointer to std::vector
is bad(or weird? not sure) in the question (Is there other syntax for this pointer operation?)
And I personally like reference to std::vector
too, so that I can use operator[]
easier, but the drawback is I have to declare an additional empty variable for it.
Code example are like: In SomeClass.h
typedef std::vector<SomeStruct> DataVec;
typedef std::map<int, DataVec> DataMap;
DataMap m_DataMap;
Now In SomeClass.cpp
:
Case 1:
namespace
{
DataVec EmptyVector;
}
DataVec& FindDatas(int key)
{
DataMap::iterator It = m_DataMap.find(key);
if (It == m_DataMap.end()) return EmptyVec;
return It->second;
}
Case 2:
DataVec* FindDatas(int key)
{
DataMap::iterator It = m_DataMap.find(key);
if (It == m_DataMap.end()) return NULL;
return &(It->second);
}
Reference:
Pros: looks like normal std::vector
.
Cons: Additional variable declared.
Pointer:
Pros: Shorter query function and no need other variable.
Cons: looks weird(?!), and you can't juse p[i]
, you have to (*p)[i]
, which is annoying.
Which one is better?
Upvotes: 7
Views: 6058
Reputation: 76428
It depends on your design requirements. If calling this function with an index that doesn't have a corresponding element is a programming error, then the code should abort. If it's a user error, it should throw an exception. If it's part of the expected usage, then you have three alternatives, again depending on your design. You can flag the problem, typically by returning a null pointer or returning a Boolean value from a function that takes a reference for the result. You can quietly return a newly created valid object, as std::set
does. You can return a sentinel object that isn't part of your container, and users will have to check whether that's what they got before they use the returned value.
Upvotes: 1
Reputation: 7919
You can also give the reference of output as a parameter, so that you can add some enumerator or bool result as a method output:
namespace
{
DataVec EmptyVector;
}
bool FindDatas(int key, DataVec& output)
{
DataMap::iterator It = m_DataMap.find(key);
if (It == m_DataMap.end()) return false;
output = It->second;
return true;
}
Upvotes: 1
Reputation: 35505
If you don't mind creating new entries for unfound keys then you can use this code:
DataVec& FindDatas(int key)
{
return m_DataMap[key];
}
An alternative approach that avoids new entries for unfound keys:
DataVec& FindDatas(int key)
{
DataMap::iterator It = m_DataMap.find(key);
if (It == m_DataMap.end()) {
// created on first unfound key and stays
// alive until the end of the program
static DataVec fEmpty;
return fEmpty;
}
return It->second;
}
Upvotes: 0