Constexpr doesn't get evaluated when computed using const variable outside function

I present two code snippets, one of them compiles and the other doesn't.

The one that doesn't compile:

class Solution {
    public:
    const int MAX_NUM = 100;
    const int MAX_SIZE = 200;
    bool canPartition(vector<int>& nums) {
        bitset<(MAX_NUM*MAX_SIZE)/2 + 1> bits(1);
        
        int sum = 0;
        for(int num: nums)
        {
            sum += num;
            bits |= bits << num;
        }
        return !(sum % 2) and bits[sum/2];
    }
};

Gives the errors:

error: non-type template argument is not a constant expression

implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function

One that does:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        const int MAX_NUM = 100;
        const int MAX_SIZE = 200;
        bitset<(MAX_NUM*MAX_SIZE)/2 + 1> bits(1);
        
        int sum = 0;
        for(int num: nums)
        {
            sum += num;
            bits |= bits << num;
        }
        return !(sum % 2) and bits[sum/2];
    }
};

I read constexpr documentation and found two things which might be issue here:

constexpr must immediately initialized.

It must have constant destruction, i.e. it is not of class type

Could you point out what's the issue here and help me understand the problem?

2 answers

  • answered 2021-09-11 18:15 cigien

    The error messages explain what's going on quite well. The template argument to the declaration of bits needs to be a constant expression. However, if you use non-static members like MAX_NUM inside a non-constexpr member function, you end up evaluating the this pointer, which is not allowed. ref:

    A core constant expression is any expression whose evaluation would not evaluate any one of the following:

    1. the this pointer, except in a constexpr function that is being evaluated as part of the expression

    When the variables like MAX_NUM are declared inside the member function, they're not members of the class, and so they can be used as constant expressions, which is why the first version compiles.

  • answered 2021-09-11 19:05 Aria

    The problem is that since Templates are evaluated at compile time, their arguments cannot be anything that the compiler can't "predict". In your first code the member variables "MAX_NUM" and "MAX_SIZE" are const values, meaning they cannot be changed after an instance of the class "Solution is made and they are initialized. But for each instance of "Solution" they can still be initialized with different, unpredictable values in a constructor. The default value that you set them equal to (respectively 100 and 200) are only used if they aren't initialized during creation of an instance. Take a look at the code below:

    #include <iostream>
    
    using std::cout;
    using std::cin;
    using std::endl;
    
    class A
    {
    private:
        const int m_foo = 1;
    public:
        A() {}
        A(int num) : m_foo(num) {}
        int foo() { return m_foo; }
    };
    
    int main()
    {
        A a1;         //a1.m_foo initialized with default value
        cout << "Enter desired m_foo for a2: ";
        int foo;
        cin >> foo;
        A a2(foo);    //a2.m_foo initialized with user input
        cout << "m_foo for a1: " << a1.foo() << endl 
            << "m_foo for a2: " << a2.foo();
        return 0;
    }
    

    As you can see the "const" member variable called "m_foo" can be any value entered by the user.

    However, when defining a const variable inside the scope of a function, that value cannot be changed and is therefor qualified as a template argument, i.e. it is similar to a "constexpr".

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum