Генетичен алгоритъм и невронни мрежи: вземане на адрес на временен [-fpermissive]

Работя върху генетично развити невронни мрежи. Написах програма с помощта на Visual Studio 2005 през 2008 г. Сега преобразувах програмата в Eclipse (Linux) и VS 2013 (Win) проекти с поддръжка на c++11. След стартиране и двата проекта дадоха същата грешка:

вземане на адрес на временен [-fpermissive]

След много търсене открих, че тази грешка възниква, тъй като новите стандарти на C++ не позволяват да се вземе адрес на временен обект. Тъй като всички обекти се създават с помощта на "new" (така че предполагам, че те трябва да останат достъпни през цялото време). Разбрах частично проблема, но не знам как да го реша. Ако стартирате проекта eclipse, той ще подчертае само две грешки в проекта.

Тъй като е трудно да се обясни с думи, тъй като се нуждае от пълен код за изследване, така че хоствах eclipse проект на git на https://bitbucket.org/a-akram/geans.git, който направих достъпен за всички.

Основният проблем е с променливите wp1 & wp2. m_vPopulatin е вектор, съдържащ адреси на невронни мрежи.

CNeuralNetwork* CGeneticEngine::Evolve()
{
    CLearningEngine *l;
    double totalError =  0.0;
    for (int iter = 0;iter < CGN_MAXITER; iter++)
    {
        for (int i = 0;i < CGN_POPULATION; i++)
        {
            l = new CLearningEngine(m_vPopulation[i]);
            l->Run(m_vTrainingDataset);
            for(unsigned int p = 0; p < m_vTrainingDataset->size(); p++)
            {
                totalError = totalError + m_vPopulation[i]->getm_dTotalNetworkError();
                totalError = totalError/m_vTrainingDataset->size();
            }
            if (totalError < CGN_THRESHOLD)
                 return m_vPopulation[i];

            m_dErrors[i] = totalError;
        }
        SortFitnesses();
        NewPopulation();
    }
    return NULL;

}

void CGeneticEngine::NewPopulation()
{
    int id1, id2;
    double temp;
    std::vector <CSynapticConnection *> *wp1;
    std::vector <CSynapticConnection *> *wp2;
    for (int i=0;i<CGN_POPULATION / 2;i++)
    {
        id1 = rand() % CGN_POPULATION / 2;
        id2 = rand() % CGN_POPULATION / 2 + CGN_POPULATION / 2;

        wp1 = &m_vPopulation[id1]->getm_vListofSynaptics();
        wp2 = &m_vPopulation[id2]->getm_vListofSynaptics();

        // Cross over the weights.
        for (int j = 0; j < 2; j++)
        {
            temp = (*wp1)[j+6]->getWeight();
            (*wp1)[j+6]->setWeight((*wp2)[j+6]->getWeight() );
            (*wp2)[j+6]->setWeight(temp);
        }

        //  adding slight genetic change due to crossover randomly.
        if (rand() % 10 < 2)
        {
            for(unsigned int j = 0; j < wp1->size();j++)
                (*wp1)[j]->changeWeight((double)(rand())/(32767/2) - 1);

            for(unsigned int j = 0; j < wp2->size();j++)
                (*wp2)[j]->changeWeight((double)(rand())/(32767/2) - 1);
         }
    }
}

Тъй като това е първият ми въпрос във форума, може да не съм формулирал добре, затова моля модераторите да не го затварят в момента. Ще го направя по-ясно, ако е необходимо. Но с файловете на проекта в ръка ще бъде много лесно за вас, момчета, да разберете проблема и решението.

Ще оценя високо вашата помощ за разрешаване на проблема... моля, уведомете ме, ако имате нужда от допълнителна информация.


person Adeel    schedule 08.03.2015    source източник
comment
C++ стандартите не позволяват вземането на адрес на временен обект Имате предвид обект в стека? Можете да вземете адреса на тези; те просто няма да са валидни, след като функцията се върне. Освен това никой няма да клонира вашия проект; моля, включете MCVE в самия въпрос.   -  person Colonel Thirty Two    schedule 08.03.2015
comment
Къде изтривате l? Използвайте интелигентни указатели.   -  person Neil Kirk    schedule 09.03.2015
comment
@Neil Kirk: Благодаря, че подчертахте интелигентните указатели. Ще използвам интелигентни указатели... тъй като това е стар код, който модифицирам към текущия стандарт C++11...   -  person Adeel    schedule 09.03.2015


Отговори (1)


Първо разгледайте това подмножество на класа CNeuralNetwork:

class CNeuralNetwork
{
  // ...
public:
  std::vector<CSynapticConnection *> getm_vListofSynaptics()
  {
    return m_vListofSynaptics;
  }

  std::vector<CSynapticConnection*> m_vListofSynaptics;
  // ...
};

Тук имате инструмент за получаване (getm_vListofSynaptics()), който връща временна стойност: копие на публичния член на данните m_vListofSynaptics.

Във функцията CGeneticEngine::NewPopulation() вземате адреса на временния обект и това задейства грешката:

wp1 = &m_vPopulation[id1]->getm_vListofSynaptics();
wp2 = &m_vPopulation[id2]->getm_vListofSynaptics();

(вижте Защо вземането на адреса на временен е незаконно? за повече подробности).

Има някои промени, които трябва да обмислите/приложите:

  • getters/setters са полезни (?) за капсулиране на поведение/скриване на вътрешното представяне на свойство, но m_vListofSynaptics е публичен, така че getter „не ви купува почти нищо“ (напр. вижте Защо да използвате гетери и сетери? / Членове на публични данни срещу Getters, Setters за малко прозрение)

  • относно грешката, бихте могли (това е само „бърза корекция“, дизайнът изглежда може да бъде променен, за да се избегне напълно получаването):

    • променете инструмента за получаване, така че да връща препратка/указател към члена на данните (членът на данните трябва да е частен)

      class CNeuralNetwork
      {
      // ...
      public:
        std::vector<CSynapticConnection *> *getm_vListofSynaptics()
        {
          return &m_vListofSynaptics;
        }
      private:
        std::vector<CSynapticConnection*> m_vListofSynaptics;
      };
      
    • директен достъп до члена на данните m_vListofSynaptics (вероятно не е добра идея):

      wp1 = &m_vPopulation[id1]->m_vListofSynaptics;
      

      (разгледайте Ако променлива има getter и setter, трябва ли да е публичен?)

person manlio    schedule 09.03.2015
comment
благодаря ви много, това реши проблема ми. Предпочитам първото ви решение, използващо функцията getter. но има друга грешка. Той е с GetID(), който е внедрен в main.cpp. Как мога да го направя достъпен за всички класове, напр. в CLayer. Това не е проблем с кодирането на един файл, но тъй като имам класове в множество файлове, така че имам нужда от тази функция, достъпна за тях. Трябва ли да го направя глобален (но не знам как). - person Adeel; 09.03.2015
comment
Можете да разгледате Как се дефинира глобална функция в C++? - person manlio; 09.03.2015
comment
...или добавете static брояч директно към CNeuron / CLayer / CSynapticConnection класовете (със съответния get() метод). - person manlio; 09.03.2015