mbed_dev
mbed_dev

Reputation: 1470

nested calls to constexpr functions

Following (generic) situation:

I am trying to call another constexpr member-function within a class, but I get the error that 'this' is not a constant expression. The question I now have is (as I am new to the constexpr idiom), is if such a use-case is not allowed. Otherwise, how would I fix it?

#include <iostream>

class cc
{
public:
    cc()=default;
    ~cc()=default;

    // public method to call th eprivate constexpr functions
    template<int varbase>
    constexpr void doSomething()
    {
        static_assert(varbase>0, "varbase has to be greater zero");
        // nested call to constexpr functions
        char x=testM2<testM1<varbase>()>();
    }

private:

    template<int var1>
    constexpr int testM1()
    {
     int temp=var1;
     return (++temp);
    }

    template<int var2>
    constexpr char testM2()
    {
        int temp=var2;
        if (temp==2)
            return 'A';
        else
            return 'B';
    }
};

int main()
{
    cc obj;
    obj.doSomething<1>();
    return 0;
}

Run the above example

Upvotes: 2

Views: 531

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275220

So constexpr does not mean "this function can only be called at compile time". It means "this function may be called both at compile and run time".

    char x=testM2<testM1<varbase>()>();

If called at run time, the value of this is not a compile time constant. So the result of a constexpr method call is not a compile time value, so its result cannot be used as a template argument.

If you are not using this (as you appear not to be) then marking testM2 etc. as static fixes your problem. If you are using this then there isn't a simple workaround; there is no way to say "this function will only be called at compile time". Any workaround will depend on details of what you are doing with this and the return value, if even possible. Write a new question with those details added.

Upvotes: 2

badola
badola

Reputation: 870

The problem is that every instance of cc will have its own version of doSomething(), testM1() and testM2().
To solve it, the compiler will apply this in front of all the functions, to identify which instance of cc you are trying to refer to.

this is not a constant expression. It is evaluated at runtime.
That is why you are getting the error : 'this' is not a constant expression

To solve this problem, either move your constexpr functions outside the class, or mark these functions as static.

Here is a working revision of your code, which will work in C++11 as well.

#include <iostream>

template<int var1>
constexpr int testM1() // move these functions outside, if they use no data of class cc
{
    return int{var1 + 1};
}

template<int var2>
constexpr char testM2()
{
    return (var2 == 2) ? 'A' : 'B';
}

class cc
{
public:
    cc()=default;
    ~cc()=default;

    // public method to call the private constexpr functions
    template<int varbase>
    static constexpr void doSomething() 
    {
        /* if you want to keep it inside class, mark it `static` 
           so that the compiler understands that it will be shared by all instances. */
        static_assert(varbase>0, "varbase has to be greater zero");
        // nested call to constexpr functions
        constexpr char x = testM2<testM1<varbase>()>(); // preferably x should be `constexpr`
    }
};

int main()
{
    cc obj;
    obj.doSomething<2>();
    return 0;
}

Upvotes: 1

Related Questions