Home > Net >  const reference to temporary variable does not work for std::function whose type does not match its
const reference to temporary variable does not work for std::function whose type does not match its

Time:08-29

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.

  • Related