I am working on some library code and I am trying to optimize my trim()
functions. To that effect I am trying to figure out how best to deal with each kind of input. Move semantics are starting to fry my brains a little. Sometimes I think I understand them... but not very often.
What I want to do with this piece of code is take advice in all areas:
Nomenclature:
I am open to suggestions for function names. For example do I name them after the type of parameter or the type of value (trim_cref
vs trim_rval
)? What do people prefer?
Unified function name based on overloads:
Also it would be nice to end up with a universal set of functions that select the most efficient method based on input parameter without having a suffix to select between them:
std::string trim(std::string s);
std::string& trim(std::string& s); // mutating
std::string trim(std::string&& s);
std::string trim(const std::string& s);
I am pretty sure that's not completely achievable due to ambiguity. The question is what version should I leave out? The copy or the mutating?
Efficiency of basic algorithm:
I am using this basic algorithm:
std::string& trim(std::string& s, const char* t = ws)
{
s.erase(0, s.find_first_not_of(t));
s.erase(s.find_last_not_of(t) + 1);
return s;
}
I would like to think the library is smart enough to special-case s.erase()
when the first parameter is zero or the last parameter is std::string::npos
. If not is there a faster way to do this?
Efficiency of calling:
I have implemented these functions in terms of each other, but I am not sure (at all) if what I have done is most efficient. Or even if I am not reducing efficiency (by inhibiting RVO for example).
Am I even on the right track?
Do I even need 4 functions (or overloads)? Would it be just as efficient to simply take parameters as copies and rely on RVO?
const char* const ws = " \t\n\r\f\v";
inline std::string& ltrim_mute(std::string& s, const char* t);
inline std::string ltrim_move(std::string&& s, const char* t);
inline std::string ltrim_cref(const std::string& s, const char* t);
inline std::string ltrim_copy(std::string s, const char* t);
inline std::string& rtrim_mute(std::string& s, const char* t);
inline std::string rtrim_move(std::string&& s, const char* t);
inline std::string rtrim_cref(const std::string& s, const char* t);
inline std::string rtrim_copy(std::string s, const char* t);
inline std::string& trim_mute(std::string& s, const char* t);
inline std::string trim_move(std::string&& s, const char* t);
inline std::string trim_cref(const std::string& s, const char* t);
inline std::string trim_copy(std::string s, const char* t);
// LEFT
inline std::string& ltrim_mute(std::string& s, const char* t = ws)
{
s.erase(0, s.find_first_not_of(t));
return s;
}
inline std::string ltrim_move(std::string&& s, const char* t = ws)
{
return std::move(ltrim_mute(s, t));
}
inline std::string ltrim_cref(const std::string& s, const char* t = ws)
{
return std::move(ltrim_move(std::string(s),t));
}
inline std::string ltrim_copy(std::string s, const char* t = ws)
{
return std::move(ltrim_move(std::move(s),t));
}
// RIGHT
inline std::string& rtrim_mute(std::string& s, const char* t = ws)
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}
inline std::string rtrim_move(std::string&& s, const char* t = ws)
{
return std::move(rtrim_mute(s, t));
}
inline std::string rtrim_cref(const std::string& s, const char* t = ws)
{
return std::move(rtrim_copy(s, t));
}
inline std::string rtrim_copy(std::string s, const char* t = ws)
{
return std::move(rtrim_move(std::move(s),t));
}
// BOTH
inline std::string& trim_mute(std::string& s, const char* t = ws)
{
return rtrim_mute(ltrim_mute(s, t), t);
}
inline std::string trim_move(std::string&& s, const char* t = ws)
{
return std::move(rtrim_mute(ltrim_mute(s, t), t));
}
inline std::string trim_cref(const std::string& s, const char* t = ws)
{
return std::move(rtrim_move(ltrim_cref(s, t), t));
}
inline std::string trim_copy(std::string s, const char* t = ws)
{
return std::move(rtrim_mute(ltrim_mute(s, t), t));
}
std::move()
. \$\endgroup\$