ошибка: неконстантное состояние c элемент данных должен быть инициализирован вне строки - PullRequest
2 голосов
/ 30 апреля 2020
class Solution {
    public:

     static int m=INT_MIN; // it shows error: non-const static data member must 
                               be initialized out of line.(why?)
                                  using "int m=INT_MIN" is fine. 
      int func(TreeNode*root){
        if(root==NULL){
            return 0;
        }

        int l=max(func(root->left),0);
        int r=max(func(root->right),0);

        m=max(l+r+root->val,m);

        return max(l,r)+root->val;

    }


    int maxPathSum(TreeNode* root) {

        if(root==NULL)
        {
         return 0;
        }
        m=INT_MIN;
        int x=func(root);
        return m;

    }
};

Мне нужно обновить значение переменной m. Поэтому я использую static int тип данных. Но следующая ошибка идет. Использование int вместо static int работает нормально. Но почему static int дает ошибку?

compilation error

Ответы [ 2 ]

2 голосов
/ 30 апреля 2020

Бьярн Страуструп объясняет это здесь :

Класс обычно объявляется в файле заголовка, а файл заголовка обычно включается во многие единицы перевода. Однако, чтобы избежать сложных правил компоновщика, C ++ требует, чтобы у каждого объекта было уникальное определение. Это правило было бы нарушено, если бы C ++ допускал в классе определение сущностей, которые должны были храниться в памяти как объекты.

Как сказал Страуструп, каждому классу необходимо уникальное определение. Теперь, как мы знаем, члены stati c напрямую связаны со своим классом. Теперь рассмотрим два случая:

  1. Элемент static также является constant, тогда его инициализация допускается встроенной, поскольку компилятор может выполнять свои собственные оптимизации и обрабатывать этот элемент как компиляцию постоянная времени, потому что гарантируется, что ее значение никогда не изменится. Таким образом, поскольку значение этого члена является фиксированным, определение класса, с которым связан этот член, также является фиксированным. Таким образом, инициализация допускается встроенная.

  2. Член static не является константой. Затем его значение может измениться позже во время выполнения программы. Таким образом, компилятор не может выполнять оптимизацию во время компиляции для этого члена. Следовательно, чтобы предотвратить осложнения, которые могут возникнуть при попытке инициализации такого члена при загрузке класса, встроенная инициализация таких членов не допускается.

PS: Когда я услышал об этой концепции в первый раз, я был также сбит с толку, потому что она не соответствует принципу ортогональности, что является особенностью, желаемой программистами. Принцип ортогональности будет утверждать, что, поскольку мы можем комбинировать int и static; и int и const, мы должны быть в состоянии написать static const int и static int аналогичным образом. Но этот случай здесь является примером ситуации, когда разработчик языка должен отказаться от ортогональности для пользователей языка в обмен на простоту процесса компиляции.

Посмотрите на концепцию ортогональность здесь

1 голос
/ 30 апреля 2020

Чтобы ответить на вопрос ОП, почему

class Solution {
  public:
    int m = INT_MIN;
};

хорошо, но

class Solution {
  public:
    static int m = INT_MIN;
};

не является:

Короче: префикс элемента данных с static принципиально измените его значение.

Без static переменная-член является частью класса, и каждый экземпляр будет предоставлять отдельное хранилище для этой переменной-члена.

При static переменная-член имеет только область видимости класса, но будет только одно глобальное хранилище.

Соответственно, инициализация также имеет разные значения.

Для нестатические c переменные-члены, он обеспечивает инициализацию по умолчанию, которую конструкторы могут использовать (или переопределить).

Демонстрация на примере:

#include <iostream>

enum ArgCase1 { Case1 };
enum ArgCase2 { Case2 };

class Solution {
  public:
    int m = 123;

    Solution() = default; // will use m(123) implicitly
    Solution(ArgCase1) { } // will use m(123) implicitly
    Solution(ArgCase2): m(456) { } // default of m ignored
};

#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  DEBUG(Solution sol);
  std::cout << "sol.m: " << sol.m << '\n';
  DEBUG(Solution sol1(Case1));
  std::cout << "sol1.m: " << sol1.m << '\n';
  DEBUG(Solution sol2(Case2));
  std::cout << "sol2.m: " << sol2.m << '\n';
}

Вывод:

Solution sol;
sol.m: 123
Solution sol1(Case1);
sol1.m: 123
Solution sol2(Case2);
sol2.m: 456

Демонстрация в реальном времени на coliru

Для static переменных-членов инициализация будет опасной. Если предположить, что класс объявлен в заголовке, который включается несколько раз, это приведет к нарушению Единого правила определения .

В прежние времена это обычно объявляли переменную-член static в заголовке, но определяли ее в .cpp -файле (который представляет единицу перевода ).

Пример:

solution.h:

#ifndef SOLUTION_H
#define SOLUTION_H

class Solution {
  public:
    static int m;
};

#endif // SOLUTION_H

solution.cc:

// header of this module:
#include "solution.h"

int Solution::m = 123;

Живая демоверсия на coliru

Начиная с C ++ 17, доступна новая альтернатива - с использованием ключевого слова inline.

From cppreference.com - stati c членов - Стати c членов данных

Статистический элемент данных c может быть объявлен встроенным. Встроенный элемент данных stati c может быть определен в определении класса и может указывать инициализатор. Не требуется внеклассное определение

Пример:

solution.h:

#ifndef SOLUTION_H
#define SOLUTION_H

class Solution {
  public:
    inline static int m = 123;
};

#endif // SOLUTION_H

Демонстрация в реальном времени включена coliru

Преимущество состоит в том, что для этого не требуется .cpp -файл, то есть, поскольку class Solution может быть предоставлен как источник только для заголовка.

...