class Context {
public:
Context(){
field2values_["age"] = std::vector<int>{1,2,3};
}
const std::vector<int>& field2values(const std::string& field) const {
auto it = field2values_.find(field);
if (it == field2values_.end()) {
return default_ints_;
}
return it->second;
}
private:
std::map<std::string, std::vector<int>> field2values_;
std::vector<int> default_ints_;
};
Context ctx;
std::vector<int> ctx_field2values(const std::string& field) {
return ctx.field2values(field);
}
class Checker {
public:
explicit Checker():
user_field_values_(ctx_field2values),
user_field_values_nc_(ctx_field2values)
{}
void print(){
const auto& values = user_field_values_("age");
std::cout << "size=" << values.size() << std::endl; // unexpected: 18446744073709535740
const auto& values_nc = user_field_values_nc_("age");
std::cout << "size=" << values_nc.size() << std::endl; // expected: 3
}
private:
const std::function<const std::vector<int>&(const std::string&)> user_field_values_;
const std::function<std::vector<int>(const std::string&)> user_field_values_nc_;
};
int main() {
Checker checker;
checker.print();
}
As we all know, const reference to temporary variable will extend the its lifetime. But in the code above, it does not work for user_field_values_
while it works for user_field_values_nc_
. I guess this is because the type of user_field_values_
does not match its initialization, namely ctx_field2values
. But why is there such a difference? Can anyone explain in principle why this rule (const reference to temporary variable) does not take effect?
Thanks in advance.
CodePudding user response:
It's the same reason the following produces a dangling reference:
int f() {
return 42;
}
const int& invoke_f() {
return f();
}
const auto& e = invoke_f(); // dangling!
Basically, when a temporary appears in a return
statement, its lifetime is not extended. It gets destroyed at the end of the return
statement. ([class.temporary]/(6.11))
The function call operator of user_field_values_
behaves just like the invoke_f
above. It invokes ctx_field2values
(which returns a vector<int>
), and returns the result as a const vector<int>&
-- a dangling reference.
In C 23, std::function
will be able to recognize this pattern (by means of std::reference_converts_from_temporary
) and reject it. But it requires compiler support, which AFAIK does not exist yet.