问题
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.
回答1:
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.
回答2:
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.
回答3:
Unfortunately, the standard states that in a class member access expression The postfix expression before the dot or arrow is evaluated;63 [expr.ref]/1. A postfix expression is a
in a.b
. The note is really interesting because this is precisely the case here:
63) If the class member access expression is evaluated, the subexpression evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.
So data
is evaluated even if it would not be necessary and the rule fore constant expression applies on it too.
来源:https://stackoverflow.com/questions/54124899/why-is-a-constexpr-function-on-a-reference-not-constexpr