Segfaults в C++ при использовании std::shared_ptr

В приведенном ниже коде при использовании std::shared_ptr я получаю ошибку сегментации. Однако при использовании обычных указателей та же проблема не возникает.

Подводя итог, я хочу определить объект ConstrainedVariable, который будет обновляться в соответствии с некоторой кривой всякий раз, когда обновляется значение ссылочной переменной. Способ, который я нашел для реализации этого, включает использование интеллектуальных указателей, поскольку они автоматически удаляются из содержащих их векторов, когда на них больше не ссылаются (например, когда переменная больше не используется).

#include <vector>
#include <functional>
#include <memory>
#include <iostream>
#include <cmath>

using Action = std::function<void()>;
using ActionPtr = std::shared_ptr<Action>;
using ActionWeakPtr = std::weak_ptr<Action>;
using VectAct = std::vector<ActionWeakPtr>;
inline void execute(ActionPtr t){(*t)();}
inline ActionPtr action(Action t) {return std::make_shared<Action>(t);}

template<class T>
class Variable {
  protected:
    T ival; // internal value
    std::vector<ActionWeakPtr> actions;

  public:
    Variable() = default;

    inline void link(ActionPtr t) {actions.push_back(t);}

    void set(T newval) {
      if (ival == newval) return;
      static_set(newval);
      force_update();
    }

    T val() {return ival;}

  private:

    void static_set(T newval){
      ival = newval;
    }

    void force_update(){
      for(auto it = actions.begin(); it != actions.end(); /* Nothing */){
        auto action = it->lock();
        if (action){execute(action); it++;}
        else {it = actions.erase(it);}
      }
    }

};

template<class T>
using VariablePtr = std::shared_ptr<Variable<T>>;

template<class T>
inline VariablePtr<T> var() {return std::make_shared<Variable<T>>();};

template<class I, class O>
using Curve = std::function<O(I)>;

template<class I, class O>
class ConstrainedVariable : private Variable<O> {
  ActionPtr constraint;
  std::weak_ptr<Variable<I>> _refVar;
  Curve<I, O> _refCurve = [](I t){return std::sin(t);};

  public:
    void applyConstraint() {
      if (auto sharedVar = _refVar.lock()){
        this->set(_refCurve(sharedVar->val()));
      }
    }

    O val() {return this->ival;}

    ConstrainedVariable(VariablePtr<I> refVar, Curve<I, O> refCurve) :
      Variable<O>(), _refVar(refVar), _refCurve(refCurve) {
      constraint = action([this](){this->applyConstraint();});

      if (auto v = _refVar.lock()){
        v->link(constraint);
      }

      execute(constraint);
    }
};

template<class I, class O>
std::shared_ptr<ConstrainedVariable<I, O>> ctrVar(VariablePtr<I> refVar, Curve<I, O> refCurve){
  return std::make_shared<ConstrainedVariable<I, O>>(ConstrainedVariable<I, O>(refVar, refCurve));
};

int main(){
  auto v  = var<float>();

  //auto _c = ConstrainedVariable<float, float>(v, [](float t){return std::sin(t);});

  //auto* c = &_c;                                                        // WORKS
  auto c  = ctrVar<float, float>(v, [](float t){return std::sin(t);});  // DOESNT WORK

  std::cout << v->val() << std::endl;
  std::cout << c->val() << std::endl;

  v->set(1);

  std::cout << v->val() << std::endl;
  std::cout << c->val() << std::endl;

}

Мне не удалось минимально воспроизвести проблему и я не понимаю, почему это происходит. Любая помощь приветствуется.

Ошибка сегментации на чем именно? Вы пробовали использовать отладчик, чтобы увидеть, в чем проблема?

Quimby 21.08.2024 15:31

Из GDB: #0 0x000000000060c050 в ?? () #1 0x0000000000402c70 в std::function<float (float)>::operator()(float) const (this=0x7fffffffd970, __args#0=-nan(0x7fd930)) в /opt/rh/devtoolset-8/ root/usr/include/c++/8/bits/std_functio‌​n.h:687 #2 0x0000000000402592 в ConstrainedVariable<float, float>::applyConstraint (this=0x7ffffffd930) в temp.cpp:67 при переходе к кадру №2 и просмотре при возврате _refVal->val() значением является -nan. Таким образом, ошибка сегмента происходит при вызове _refCurve для этого значения nan.

Pedro Xavier 21.08.2024 15:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в ctrVar. Вы создаете временный ConstrainedVariable в параметрах make_shared, который затем копируется в указатель.

Поскольку действие constraint инициализируется с помощью захвата лямбды this, оно зависает, когда оригинал перестает существовать. ConstrainedVariable копировать или перемещать небезопасно, поэтому вам следует = delete использовать эти специальные функции-члены или исправить их, чтобы они регистрировались при копировании или перемещении.

Непосредственное решение — передать параметры конструктора make_shared, чтобы вы не копировали ConstrainedVariable в свой образец.

template<class I, class O>
std::shared_ptr<ConstrainedVariable<I, O>> ctrVar(VariablePtr<I> refVar, Curve<I, O> refCurve){
  return std::make_shared<ConstrainedVariable<I, O>>(refVar, refCurve);
};

Спасибо! Именно в этом и была проблема! Еще один вопрос для моего личностного роста: плох ли «беспорядок указателей»? Я не могу избавиться от ощущения, что я делаю что-то не так в своем дизайне.

Pedro Xavier 21.08.2024 15:55

Ничего особенного, просто у меня были небольшие проблемы с поиском того, какой из них висит. Смотри редактирование, я только что нашел его

Caleth 21.08.2024 15:58

Если я правильно понял ваш вариант использования, возможно, вы захотите использовать std::atomicstd::shared_ptr в шаблоне ctrVar вместо простого Shared_ptr.

Другие вопросы по теме