7

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?

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • @LightnessRacesinOrbit: This one : [&(std::cout)-as-template-argument](http://stackoverflow.com/questions/39776437/c-stdcout-as-template-argument) – Jarod42 Sep 29 '16 at 18:35
  • Sorry, yeah. I figured a new more straightforward question about the why was in order, since the answer to the how in the old question was just "remove the parentheses". – aschepler Sep 29 '16 at 18:37
  • I'm guessing that this has to do with the fact that adding parentheses around the name of an object makes the expression yield an rvalue rather than an lvalue. – templatetypedef Sep 29 '16 at 18:38
  • 1
    @LightnessRacesinOrbit I'm not sure the answer there answers the question. It links to a question specifically about pointers to members, but `std::cout` is an object. – Barry Sep 29 '16 at 18:39
  • 1
    @templatetypedef If it were that simple, you couldn't do `int f(int&); int x; int y = f((x));` – aschepler Sep 29 '16 at 18:41
  • @Barry: Not saying it does – Lightness Races in Orbit Sep 29 '16 at 18:47
  • @LightnessRacesinOrbit _"That's the devil!"_ Well, that leaves me appearing in a bad light. Maybe I should remove my answer (the dupe vote was removed already, although the OP stated it helped them). – πάντα ῥεῖ Sep 29 '16 at 19:19
  • @πάνταῥεῖ: In this context, my phrase just means "That's the one!" – Lightness Races in Orbit Sep 29 '16 at 19:34

1 Answers1

4

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]).
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Hmm. Is N4296 not close to C++14? In N4296, [temp.arg.nontype] basically just defers to "converted constant expression" with a few restrictions. – aschepler Sep 29 '16 at 18:52
  • @aschepler I think N4296 is one of the post-C++14 working drafts. Also see the edits to my answer: I think the wording you're seeing is in C++17. – Brian Bi Sep 29 '16 at 18:53