Considere esta pequena aula destinada a coletar uma sequência de 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) );
}
};
Eu pretendo usar isso como:
issues_t issues;
do_something(issues);
Isso funciona:
void do_something(issues_t& notify_issue)
{
notify_issue("something");
}
Mas prefiro consumir isso como:
using fnotify_t = std::function<void(std::string&&)>;
void do_something(fnotify_t const& notify_issue)
{
notify_issue("something");
}
Isso compila, mas não funciona: no depurador, o push_back()
é executado, mas o vetor sublinhado não é preenchido ( godbolt ), então o que estou perdendo aqui?
exemplo reproduzível mínimo:
#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_something
aceita umconst&
que pode ser vinculado a objetos temporários. Ao chamardo_something
, um objeto temporáriostd::function
é construído e a"something"
string é armazenada no temporárioissues_t
que ele contém, não no originalissues_t issues
.Seu design está sujeito a esses erros, e trabalhar com modelos e lambdas é provavelmente mais conveniente:
Mais idiomaticamente,
do_something
deve ser semelhante a:Qualquer uma dessas "melhorias" (1, 2, 3) não é estritamente necessária, mas é mais idiomática.