aschepler aschepler - 2 months ago 21
C++ Question

Do parentheses make a pointer template argument invalid?

Consider this code:

int x = 0;

template<int& I>
struct SR {};

template<int* I>
struct SP {};

SR<(x)> sr;
SP<&(x)> sp;

int main(void)
{
}


clang++ 3.8.0 complains:

main.cpp:10:5: error: non-type template argument does not refer to any declaration
SP<&(x)> sp;
^~~
main.cpp:6:15: note: template parameter is declared here
template<int* I>
^


g++ 6.1.0 complains:

main.cpp:10:8: error: template argument 1 is invalid
SP<&(x)> sp;
^


Naturally, everything works fine if I remove the parentheses, as in
SP<&x> sp;
. But I can't find anything in the C++14 Standard that would make a difference here. Further, why is the reference case okay but the pointer case bad? Are the compilers correct to reject the program?

Answer

My reading of the standard is that it should be allowed, but I suppose the implementors of GCC and Clang disagree with my interpretation, and they're probably correct. For a definitive answer, it might be a good idea to ask the std-discussion@isocpp.org mailing list (I'll shoot them an email). The wording might be a defect.

According to [temp.arg.nontype], one of the possible forms for a non-type template argument is:

... 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 ...

It all hinges on what "ignoring parentheses" means. GCC and Clang both accept (&x) but not &(x); they seem to have decided that "ignoring parentheses" means only on the outside, not around the id-expression. If this was the intent of the standards committee, the language should be clarified.

Edit: In the C++17 draft, this is unambiguously allowed, since the form of allowable non-type template arguments has been relaxed considerably:

A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject ([intro.object]),
  • a temporary object ([class.temporary]),
  • a string literal ([lex.string]),
  • the result of a typeid expression ([expr.typeid]), or
  • a predefined __func__ variable ([dcl.fct.def.general]).
Comments