Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a constexpr function on a reference not constexpr?

Consider the following function:

template <size_t S1, size_t S2>
auto concatenate(std::array<uint8_t, S1> &data1, std::array<uint8_t, S2> &data2) {
    std::array<uint8_t, data1.size() + data2.size()> result;

    auto iter = std::copy(data1.begin(), data1.end(), result.begin());
    std::copy(data2.begin(), data2.end(), iter);

    return result;
}

int main()
{
    std::array<uint8_t, 1> data1{ 0x00 };
    std::array<uint8_t, 1> data2{ 0xFF };

    auto result = concatenate(data1, data2);
    return 0;
}

When compiled using clang 6.0, using -std=c++17, this function does not compile, because the size member function on the array is not constexpr due to it being a reference. The error message is this:

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

When the parameters are not references, the code works as expected.

I wonder why this would be, as the size() actually returns a template parameter, it could hardly be any more const. Whether the parameter is or is not a reference shouldn't make a difference.

I know I could of course use the S1 and S2 template parameters, the function is merely a short illustration of the problem.

Is there anything in the standard? I was very surprised to get a compile error out of this.

like image 372
Martijn Otto Avatar asked Jan 10 '19 08:01

Martijn Otto


People also ask

Can a constexpr function call a non constexpr function?

A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.

Why is constexpr over Const?

Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors. constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.

Is constexpr function always inline?

2) A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is always inline. 3) A function declared constexpr is always inline.


2 Answers

Because you have evaluated a reference. From [expr.const]/4:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • ...
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is usable in constant expressions or
    • its lifetime began within the evaluation of e;
  • ...

Your reference parameter has no preceding initialization, so it cannot be used in a constant expression.

You can simply use S1 + S2 instead here.

like image 149
xskxzr Avatar answered Sep 28 '22 04:09

xskxzr


There has been a bug reported on this issue for clang titled: Clang does not allow to use constexpr type conversion in non-type template argument.

The discussion in it points that this is not really a bug.

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • [...]
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is initialized with a constant expression or
    • its lifetime began within the evaluation of e;
  • [...]

The above quote is from [expr.const]/2.11 of draft n4659 with emphasis added.

like image 24
P.W Avatar answered Sep 28 '22 03:09

P.W