Search code examples
c++c++17constexprnon-type-template-parameterconstexpr-function

literal type in constexpr expression and template parameter


Why do I can use non constexpr literal types in constexpr functions(such as reflection) and it can be returned as constexpr, but I can't use such types in template non-type parameters?

class Point {
    public:
        constexpr Point(double xVal = 0, double yVal = 0) noexcept
        : x(xVal), y(yVal)
        {}

        constexpr double xValue() const noexcept { return x; }
        constexpr double yValue() const noexcept { return y; }

        constexpr void setX(double newX) noexcept { x = newX; }
        constexpr void setY(double newY) noexcept { y = newY; }
    private:
        double x, y;
};

template <long long N>
void F()
{
    std::cout << N << std::endl;
}

constexpr Point reflection(const Point& p) noexcept
{
    Point result;
    result.setX(p.xValue());
    result.setY(p.yValue());
    return result; // returning literal non consexpr type
}
 
int main()
{
    constexpr Point p;
    F<static_cast<long long>(reflection(p).xValue())>(); //result returned from reflection can be used here

    Point p1;
    p1.setX(123);
    F<static_cast<long long>(p1.xValue())>(); //error: the value of ‘p1’ is not usable in a constant expression
}

Solution

  • constexpr is not a property of a type. It is a specifier on a variable/function declaration.

    Objects whose lifetime begins within the evaluation of the constant expression are usable in that constant expression and don't need to be declared constexpr.

    The expression that needs to be a constant expression here is in the first case

    static_cast<long long>(reflection(p).xValue())
    

    The variable result inside reflection lives only during the evaluation of that expression and p is declared constexpr. Therefore both are usable in the constant expression.

    In the second case the expression

    static_cast<long long>(p1.xValue())
    

    is required to be a constant expression. This uses p1, but p1 is not declared constexpr and its lifetime started before the evaluation of the expression, so it is not usable in the constant expression. More precisely the lvalue-to-rvalue conversion required in xValue() violates the requirements for a constant expression.