13

Is it legal to declare a reference template in C++14 without initializing the primary reference template, as long as it is never instantiated?

template<class T>
const T& ref;

template<>
auto ref<int> = 1;

auto x = ref<int>;

This produces different results on GCC and Clang:

$ g++ -std=c++14 -c ref.cpp
$

$ clang -std=c++14 -c ref.cpp
ref.cpp:2:10: error: declaration of reference variable 'ref' requires an
      initializer
const T& ref;
         ^~~
1 error generated.

It makes no sense having to initialize the primary reference template, because until it's instantiated, it's a template, not a reference.

I have found that I can do something like:

template<class T>
const T& ref = "Meaningless initialization with any value of any type";

template<>
auto ref<int> = 1;

auto x = ref<int>;

because apparently GCC and Clang both accept but ignore the reference template initializer RHS as long as it's a valid expression and the primary reference template is never instantiated. And any expression of any type satisfies Clang's initialization requirement.

GCC does not require an initializer as long as the primary reference template is never instantiated. That seems to be the correct behavior "in spirit", because until a reference template is actually instantiated, it should not need an initializer.

The Standard isn't 100% clear on reference templates. Here's what little I could find on variable template instantiation:

14.7.1

Unless a variable template specialization has been explicitly instantiated or explicitly specialized, the variable template specialization is implicitly instantiated when the specialization is used.

...

An implementation shall not implicitly instantiate ... a variable template ... that does not require instantiation.

14.7.2

Except for inline functions, declarations with types deduced from their initializer or return value (7.1.6.4), const variables of literal types, variables of reference types, and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer. [ Note: The intent is that an inline function that is the subject of an explicit instantiation declaration will still be implicitly instantiated when odr-used (3.2) so that the body can be considered for inlining, but that no out-of-line copy of the inline function would be generated in the translation unit.—end note ]

14.7.3

A declaration of a function template, class template, or variable template being explicitly specialized shall precede the declaration of the explicit specialization. [ Note: A declaration, but not a definition of the template is required. —end note ].

Edit to add:

A variable template declaration, class template declaration, or function template declaration, is not the same as a variable declaration, class declaration, or function declaration, respectively, and is not subject to the same rules. Until a template is instantiated, it is just a template.

Class templates, variable templates, and function templates may be declared without providing a primary definition, only specialization definitions. The following code is legal on both Clang and GCC:

// Class
template<class T> class foo;        // Declaration, not definition
template<> class foo<int> {};       // Specialization definition
using ifoo = foo<int>;              // Specialization instantiation

// Function
template<class T> void bar(T);      // Declaration, not definition
template<> void bar(int) {}         // Specialization definition
void (*barp)(int) = bar<int>;       // Specialization instantiation

// Variable
int j;
template<class T> T* point;         // Declaration, not definition
template<> int* point<int> = &j;    // Specialization definition
int *k = point<int>;                // Specialization instantiation

The question, then, is why should it be any different for a reference template? Why should the primary declaration of a reference template have to be a definition with a reference initialization, when that's not true of any other templates?

template<class T> const T& ref;      // Declaration, not definition
template<> const int& ref<int> = 1;  // Specialization definition
const int& iref = ref<int>;          // Specialization instantiation
leek
  • 861
  • 8
  • 12
  • 3
    A declaration of a variable is a definition unless it is declared with the `extern` keyword. If you mean a declaration, you can express it: `template extern const T& ref;`. The extern will not change the linkage. – Oliv Nov 29 '18 at 07:20
  • This is a declaration of a variable template, not of a variable. – leek Nov 29 '18 at 22:52
  • The comment could have been more pedantic, general and less accessible indeed: [basic.def](http://eel.is/c++draft/basic.def#2.2) – Oliv Nov 30 '18 at 11:41

1 Answers1

8

I believe this is covered by [temp.res]/8:

... The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated...

The reference template you've written can never yield a valid specialization, as the variable produced by an instantiation will always require an initializer.


The quotation I provided is from C++17, but there's a similar statement in C++14.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I don't believe that rule applies here. First, 14.6 is talking about name resolution inside of template definitions, which we're not talking about here -- `const T&` does not reference any names outside of the template definition. Second, the `ref` generates a valid specialization in the only place the `ref` template is referenced. The `ref` specialization is instantiated. There is no reference in the program to the `ref` template which, when looked up, cannot be instantiated. That's why GCC does not care if the primary template is defined, because it never needs to be instantiated. – leek Nov 29 '18 at 23:13
  • @leek What if we were defining a function template and `const T& ref;` appeared in the body? Can you see why a compiler would want to reject that? The case in your question is no different. Since references require initialization, this kind of statement can be viewed as *syntactically* incorrect. – Brian Bi Nov 30 '18 at 00:29
  • As I told @Oliv, this is a variable template declaration, not a variable declaration. To use your analogy, if you declare `template void foo(T);` as a function template declaration (not a function declaration), and then you specialize `template<> void foo(int x) { }`, there is no need to define the primary function template in order to instantiate and call `foo();`. Templates are not objects/functions/types, so their declaration/definition rules are not the same as objects, functions or types. Until they're instantiated, implicitly or explicitly, they are just templates. – leek Nov 30 '18 at 01:00
  • `template void foo(T) { const T&ref; }` compiles just fine on GCC, but not on Clang, again for maybe the same reasons as OQ. It's a function template definition, not a function definition. Until it's instantiated, the fact that `const T& ref` appears without an initializer is irrelevant, under one interpretation of the standard (which GCC seems to use). In the case of reference templates it becomes more important than this example because we may want specializations without a default definition, which is legal for class templates, function templates and non-ref variable templates. – leek Nov 30 '18 at 01:12
  • @leek Nevertheless, the standard says that if you write a template with the property that every possible instantiation of it is invalid, then the program is ill-formed. If it's impossible for you to *define* the template in a way that satisfies this rule, then you're free to simply *declare* it, as in the case of `template void foo(T);`, and then define specializations only when you can. You can also declare your variable template with `extern`, and define it only when you can. – Brian Bi Nov 30 '18 at 02:17
  • "A template with the property that every possible instantiation of it is invalid" does not apply to this case. The specialization `ref` is a valid instantiation. You seem to be confusing template declarations with template instantiations. The primary declaration of a template is not necessarily instantiated. "You're free to simply declare it... and then define specializations" is precisely what is being done. There is no rule that this is only allowed when it is "impossible to satisfy " in the primary template (whatever that rule is -- you didn't specify it). – leek Nov 30 '18 at 04:23
  • 3
    @leek No, you're misinterpreting the standard where it says "no valid specialization can be generated for a template". What it means is that no specialization *produced by instantiating the template* is valid. An explicit specialization doesn't count. – Brian Bi Nov 30 '18 at 04:27
  • The quoted standard says that if no valid specialization can be generated **but** it's not instantiated, it's an ill-formed definition, but no diagnostic is required (because it's not being instantiated and therefore is not used -- it's no-op code). If it's instantiated, then a diagnostic is required. The quote says nothing about a primary template *declaration* being required to be a template *definition*, or that the primary template declaration must, as a definition, be specialized and implicitly instantiated at least once, or else the program is ill-formed. – leek Nov 30 '18 at 05:34
  • @leek Your declaration `template const T& ref` is a definition for syntactic reasons. See [basic.def]/2. – Brian Bi Nov 30 '18 at 15:56
  • "*The reference template you've written can never yield a valid specialization*". It can: `ref` is a valid specialization so I don't think this sentence applies – Rakete1111 Dec 01 '18 at 23:46
  • @Rakete1111 As I pointed out previously, explicit specializations don't count for the purposes of this rule. – Brian Bi Dec 01 '18 at 23:58