4
#include <iostream>

using namespace std;

template<const int arr[]>
struct S {
    static constexpr int value = arr[0];
};

constexpr int arr[] = { 5 };

int main() {
    cout << S<arr>::value << endl;
}

This program compiles fine and prints 5 with gcc 5.1 and higher, but MSVC 19.10.25019 gives the following errors:

error C2975: 'S': invalid template argument for 'arr', expected compile-time constant expression error C2131: expression did not evaluate to a constant

Is this program valid according to the C++14 Standard, or gcc is being too lenient here?

3
  • For the record, Clang accepts this code, too. Commented Apr 20, 2017 at 19:27
  • What does MSVC say if you replace arr[] with arr[1]? Commented Apr 20, 2017 at 19:29
  • @jwimberley It does not change anything. Commented Apr 20, 2017 at 19:29

1 Answer 1

2

The program is well-formed as far as I can see.

According to [temp.param]/8 the template parameter actually has type const int*, not const int[].

A non-type template-parameter of type “array of T” or “function returning T” is adjusted to be of type “pointer to T” or “pointer to function returning T”, respectively.

According to [temp.arg.nontype]/1, we can use the name of a complete array object with static storage duration and external linkage as an argument to such a template parameter:

A template-argument for a non-type, non-template template-parameter shall be one of:

...

— a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference ...

arr is a constant expression, despite the fact that MSVC thinks it isn't. It is a core constant expression according to [expr.const]/2 since it does not contain any forbidden evaluations, and it is a constant expression since it points to an object with static storage duration ([expr.const]/4).

Because the template parameter refers to an array with static storage duration, the bounds of the array are known at the time of template instantiation. It can therefore verify that the access to arr[0] is a legitimate core constant expression since it has well-defined behaviour and falls into the category of allowed lvalue-to-rvalue conversions in [expr.const]/2:

... a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression

Sign up to request clarification or add additional context in comments.

1 Comment

Technically, it violates [temp.arg.nontype]/2.1: "For a non-type template-parameter of ... pointer type, the value of the constant expression shall not ... be the address of... a subobject". The pointer points to the first element of the array, which is a subobject. But that's a known problem with the current wording; see CWG 2043.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.