Consider this small class aimed to collect a sequence of strings:
class issues_t final
{
private:
std::vector<std::string> m_issues;
public:
constexpr void operator()(std::string&& txt)
{
m_issues.push_back( std::move(txt) );
}
};
I plan to use this as:
issues_t issues;
do_something(issues);
This works:
void do_something(issues_t& notify_issue)
{
notify_issue("something");
}
But I'd rather like to consume this as:
using fnotify_t = std::function<void(std::string&&)>;
void do_something(fnotify_t const& notify_issue)
{
notify_issue("something");
}
This compiles but doesn't work: in the debugger the push_back() is executed but the underlining vector is not populated (godbolt), so what I'm missing here?
minimum reproducible example:
#include <string>
#include <vector>
#include <functional>
#include <print>
class issues_t final
{
private:
std::vector<std::string> m_issues;
public:
constexpr void operator()(std::string&& txt)
{
m_issues.push_back( std::move(txt) );
}
[[nodiscard]] constexpr auto begin() const noexcept { return m_issues.cbegin(); }
[[nodiscard]] constexpr auto end() const noexcept { return m_issues.cend(); }
};
using fnotify_t = std::function<void(std::string&&)>;
void do_first(fnotify_t const& notify_issue)
{
notify_issue("do_first");
}
void do_second(issues_t& notify_issue)
{
notify_issue("do_second");
}
int main()
{
issues_t issues;
issues("main");
do_first(issues);
do_second(issues);
for( const auto& issue : issues )
{
std::print(" {}\n", issue);
}
}
do_somethingaccepts aconst&which can bind to temporary objects. When callingdo_something, a temporarystd::functionobject is constructed, and the"something"string is stored within the temporaryissues_tit contains, not within the originalissues_t issues.Your design is prone to such mistakes, and working with templates and lambdas is likely more convenient:
More idiomatically,
do_somethingshould look like:Any one of these "improvements" (1, 2, 3) aren't strictly necessary, but more idiomatic.