error: non-const static data member must be initialized out of line
Asked Answered
U

3

18
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;

    }
};

I need to update the value of variable m. Therefore I am using static int data type. But the following error is coming. Using int instead of static int is working fine. But why is static int giving error?

compilation error

Unlikelihood answered 30/4, 2020 at 7:42 Comment(10)
Your posted code does not use static anywhere, please show us the code that actually generates that error.Illona
Why don't you just use a global variable? (And why is this a class?)Hyonhyoscine
Please copy-paste the errors as text into the question. Then make sure that the minimal reproducible example you show actually causes the error. And please add a comment on the line where you get the error.Garlaand
"I need to update the value of variable m. Therefore I am using "static int" data type." - What does this even mean? Do you mean keeping it shared across all instances?Overlie
I don't think I understand your reasoning for using static data member. static data members are shared between all instances of the class, do you really want every single object of your class to change value of m?Fassold
I have commented to the line which shows the error.Unlikelihood
I have commented the line which shows the error. I know that I should not use static as it will be static for all the instances but what is the meaning of this error- "non-const static data member must be initialized out of line"Unlikelihood
What about static inline int m=INT_MIN;?Winnipegosis
Definition of static variables in header files isn't a good thing in general. The header might be included more than once and so you get an ODR violation. (ODR - One Definition Rule). Qualifying this inline tells the compiler explicitly to sort it out by itself. FYI: Static data members.Winnipegosis
Btw. are you aware that a static member variable is very different from a non-static? A static member variable only has the scope of the class but there is only one global storage. I.e. if one instance changes the value it will be changed for all other instances as well (as static member variables actually doesn't belong to any instance).Winnipegosis
P
16

Bjarne Stroustrup explains this here:

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

As said by Stroustrup, every class needs a unique definition. Now, as we know static members are associated directly with their class. Now consider the two cases:

  1. The static member is also constant, then its initialization is allowed inline because the compiler can make its own optimisations and treat this member as a compile-time constant because it is guaranteed that its value will never change. So, as the value of this member is fixed, the definition of the class with which this member is associated is also fixed. So, the initialization is allowed inline.

  2. The static member is not constant. Then its value can change later on during the execution of the program. So, the compiler can not make compile-time optimisations on this member. Hence, to prevent the complications that may arise while trying to initialize such a member when the class is loaded, inline initialisation of such members is not allowed.

PS: When I heard about this concept the very first time, I was also confused because it is not in accordance with the principle of orthogonality that is a feature desired by programmers. The principle of orthogonality will state that since we can combine int and static; and int and const, we should be able to write static const int and static int in a similar fashion. But this case here is an example of a situation where the developer of a language has to give up orthogonality for the users of the language in exchange of the simplicity of the compilation process.

Have a look at the concept of orthogonality here

Paulson answered 30/4, 2020 at 8:35 Comment(0)
R
26

To answer OPs question why

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

is fine but

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

is not:

In short: Prefixing a data-member with static fundamentally change its meaning.

Without static, the member variable is part of the class and each instance will provide a separate storage for this member variable.

With static, the member variable has only scope of the class but there will be only one global storage.

Respectively, the initialization has different meanings too.

For non-static member variables, it provides a default initialization which constructors may use (or override).

Demonstration by Example:

#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';
}

Output:

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

Live Demo on coliru

For static member variables, the initialization would be dangerous. Assuming that a class is declared in a header which is included multiple times, this would result in a violation of the One Definition Rule.

In former times, it was usual to declare the static member variable in the header but to define it in the .cpp-file (which represents the translation unit).

Expample:

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;

Live Demo on coliru

Since C++17, a new alternative is available – using the keyword inline.

From cppreference.com – static members – Static data members

A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition

Example:

solution.h:

#ifndef SOLUTION_H
#define SOLUTION_H

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

#endif // SOLUTION_H

Live Demo on coliru

The advantage is that there is no .cpp-file needed for this i.e. as it is the class Solution could be provided as header-only source.

Representational answered 30/4, 2020 at 8:47 Comment(1)
the inline declaration did the job! thx.Belisle
P
16

Bjarne Stroustrup explains this here:

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

As said by Stroustrup, every class needs a unique definition. Now, as we know static members are associated directly with their class. Now consider the two cases:

  1. The static member is also constant, then its initialization is allowed inline because the compiler can make its own optimisations and treat this member as a compile-time constant because it is guaranteed that its value will never change. So, as the value of this member is fixed, the definition of the class with which this member is associated is also fixed. So, the initialization is allowed inline.

  2. The static member is not constant. Then its value can change later on during the execution of the program. So, the compiler can not make compile-time optimisations on this member. Hence, to prevent the complications that may arise while trying to initialize such a member when the class is loaded, inline initialisation of such members is not allowed.

PS: When I heard about this concept the very first time, I was also confused because it is not in accordance with the principle of orthogonality that is a feature desired by programmers. The principle of orthogonality will state that since we can combine int and static; and int and const, we should be able to write static const int and static int in a similar fashion. But this case here is an example of a situation where the developer of a language has to give up orthogonality for the users of the language in exchange of the simplicity of the compilation process.

Have a look at the concept of orthogonality here

Paulson answered 30/4, 2020 at 8:35 Comment(0)
S
0

What you can do is at the place where you are calling the respective functions, first, initialize an int and then pass that int by address into the class methods. Edit the class method definitions accordingly.

int main(){
     int m=INT_MIN;
     Solution *solution = new Solution;
     solution->func(root,&m);
}
Saharanpur answered 19/5, 2023 at 19:6 Comment(1)
This approach requires the user of the class to know how to correctly initialize it (in this case to INT_MIN). It would be better if the correct value was set by the class itself. That said, there's no need to pass the variable by address: doing it allows the class to modify the original value inside main, but you are trying to do the opposite. For that, passing by value is enough.Detention

© 2022 - 2024 — McMap. All rights reserved.