Безопасно ли изменять функцию-указатель (std:: function) внутри вызываемой функции?

У меня есть std::function, указывающий на функцию. Внутри этой функции я меняю указатель на другую функцию.

std::function<void()> fun;

void foo() {
    std::cout << "foo\n";
}

void bar() {
    std::cout << "bar\n";
    fun = foo;
}

int main() {
    fun = bar;
    fun();
    fun();
}

Я не вижу никакой проблемы, и она работает нормально (см. здесь), однако я не уверен, что это законно Сделай так. Есть ли что-то, чего я не хватает, чтобы этого не сделать (возможно, в стандартном черновике С++ (я быстро проверил, но ничего не видел))?

Ответ 1

Это законно с указателями функций.

Когда вы назначаете или создаете std::function с целью, он создает копию цели. В случае назначения функции std::function, это фактически сохраняет указатель функции в качестве целевого объекта.

При вызове operator() требуется вернуть то, что произойдет, если вы вызовете цель с аргументами.

Внутри этого "тела" копии объекта функции, сохраненного как копия в std::function, если вы переназначаете на std::function, это уничтожит старый объект целевой функции.

Уничтожение указателя функции не влияет на достоверность кода, выполняемого в указанной функции.

Однако, если вы сохранили объекты функции (lambdas, manual, другие std::function s, std::bind и т.д.), то в точке назначения вы столкнулись с обычными правилами запуска метода в классе когда this уничтожается. Короче говоря, вы больше не сможете делать ничего, что полагалось бы на "локальное состояние" вашего экземпляра.

std::function<void()> fun;
struct bob {
  std::string name;
  bob* next = 0;
  void operator()() const {
    std::cout << name << "\n";
    if (next) fun = *next;
    // undefined behavior:
    // std::cout << name << "\n";
  }
};
bob foo = {"foo"};
bob bar = {"bar", &foo};

int main() {
  fun = bar;
  fun();
  fun();
}

живой пример.

Итак, как вы можете видеть, это может быть хрупким.

Ответ 2

Он может вернуться, чтобы укусить вас, если вы сделаете это без должного внимания и в документации по коду, но нет логической причины, почему это не сработает.

В С++ адрес функции не нужен, либо внутри функции в обратном коде.

Если это не сработало на каком-то языке, то компилятор, вероятно, не согласился бы с этим - если он будет наполовину достойным компилятором.