Date: Tue, 22 Jul 2025 13:05:27 +0100
Sometimes we want the return value from a function not only to not be
discarded, but more specifically we want it to be stored inside a
local variable.
Consider a method like std::synchronized_value::rlock, which returns
by value a type similar to "std::lock_guard". We always want this
return value to be stored in a variable:
auto mylock = mysyncvalue.wlock();
If the programmer is only going to call one method on it, then they
should instead use "operator->", therefore we can consider the
following to be incorrect:
mysyncvalue.wlock()->DoSomething();
and instead it should be replaced with:
mysyncvalue->DoSomething();
Therefore, it would make sense to mark the 'wlock' method as [[must_store]].
Another example is the wxWidgets library, with the class wxString. The
method, "wxString::c_str" looks like this:
wxCStrData wxString::c_str() const
{
return wxCStrData(this);
}
When you invoke "c_str" on a wxString object, it returns a wxCStrData
object. The wxCStrData class has two implicit conversions:
operator const wchar_t*() const { return AsWChar(); }
operator const char*() const { return AsChar(); }
If you build the wxWidgets library in Unicode mode, then the method
"AsWChar" returns a pointer to a pre-existing array. However if you
build the wxWidgets library in ASCII mode, and then invoke "AsWChar",
it dynamically allocates a temporary array and gives you the pointer.
This could allow us to put a very interesting bug into our code:
wxString mystr( wxS("Hello I'm a monkey") );
wchar_t *const p = mystr.c_str();
wcout << p << endl;
The above code will work fine on Unicode, but might crash on ASCII.
The dynamically-allocated array gets destroyed when "c_str()" returns.
The solution is to do this:
wxString mystr("Hello I'm a monkey");
auto const converter = mystr.c_str();
wchar_t *const p = converter;
wcout << p << endl;
If we can mark a function as [[must_store]] then it will prevent stuff
like this from making its way into the release build.
discarded, but more specifically we want it to be stored inside a
local variable.
Consider a method like std::synchronized_value::rlock, which returns
by value a type similar to "std::lock_guard". We always want this
return value to be stored in a variable:
auto mylock = mysyncvalue.wlock();
If the programmer is only going to call one method on it, then they
should instead use "operator->", therefore we can consider the
following to be incorrect:
mysyncvalue.wlock()->DoSomething();
and instead it should be replaced with:
mysyncvalue->DoSomething();
Therefore, it would make sense to mark the 'wlock' method as [[must_store]].
Another example is the wxWidgets library, with the class wxString. The
method, "wxString::c_str" looks like this:
wxCStrData wxString::c_str() const
{
return wxCStrData(this);
}
When you invoke "c_str" on a wxString object, it returns a wxCStrData
object. The wxCStrData class has two implicit conversions:
operator const wchar_t*() const { return AsWChar(); }
operator const char*() const { return AsChar(); }
If you build the wxWidgets library in Unicode mode, then the method
"AsWChar" returns a pointer to a pre-existing array. However if you
build the wxWidgets library in ASCII mode, and then invoke "AsWChar",
it dynamically allocates a temporary array and gives you the pointer.
This could allow us to put a very interesting bug into our code:
wxString mystr( wxS("Hello I'm a monkey") );
wchar_t *const p = mystr.c_str();
wcout << p << endl;
The above code will work fine on Unicode, but might crash on ASCII.
The dynamically-allocated array gets destroyed when "c_str()" returns.
The solution is to do this:
wxString mystr("Hello I'm a monkey");
auto const converter = mystr.c_str();
wchar_t *const p = converter;
wcout << p << endl;
If we can mark a function as [[must_store]] then it will prevent stuff
like this from making its way into the release build.
Received on 2025-07-22 12:05:41