39

According to C++1y/C++14 N3690, does the type of a variable template specialization have to be the same as the type of the primary template?

template<int x>
char y = f(x);

template<>
double y<42> = g();

And if so, is it possible to leave the primary undefined somehow?

template<int x>
???? y = ???; // undefined

template<>
double y<42> = g();

Where is this covered in the draft?

The equivalent functionality for a class template would be:

template<int x>
struct S
{
    static char y;
};

template<>
struct S<42>
{
    static double y;
};

and

template<int x>
struct S; // undefined

template<>
struct S<42>
{
    static double y;
};
Martin J.
  • 5,028
  • 4
  • 24
  • 41
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319

5 Answers5

38

Does the type of a variable template specialization have to be the same as the type of the primary template?

No, an explicit (or partial) specialization of a variable template can specify a type different from the type that would be implied by an implicit instantiation. When implementing the feature for Clang, we discovered that the specification had no rule requiring the type to match in this case, and we brought the issue to the C++ core working group, where it was confirmed that this omission was intentional.

Is it possible to leave the primary undefined somehow?

It is not possible to declare the primary variable template without specifying a type -- there is no syntax that would allow such a thing.

Where is this covered in the draft?

Both of these are covered by omission -- there is no rule requiring the types to match, and there is no syntax for declaring a variable template without a type. So I can't point you at any particular part of the standard and say "here's where the rule isn't".

If you have access to the C++ standard committee's reflectors, see the thread starting with core-23901 for the discussion of this.

Richard Smith
  • 13,696
  • 56
  • 78
  • 3
    Thanks Richard. I suppose we could propose `template void x;` as a way to declare an uninstantiable template variable primary. Maybe there is a better way without a core change. – Andrew Tomazos Jan 23 '14 at 23:48
  • @AndrewTomazos I think we need a core change to get around the rule that a template that has no valid specializations is ill-formed (no diagnostic required) in **[temp.res]p8**, or we need to use a syntax that could theoretically have valid specializations, like `template typename undefined_template::type x;`. – Richard Smith Jan 29 '14 at 20:14
  • @RichardSmith, I think `std::enable_if` is exactly for that, see my answer: https://stackoverflow.com/a/67428268/225186 – alfC May 07 '21 at 02:35
  • @alfC, a sufficiently smart compiler could see that your primary template has no valid instantiations, because `sizeof(T*)` is never zero; I think your approach is formally IFNDR. – Richard Smith May 10 '21 at 20:39
  • @RichardSmith the short answer is probably yes. The long (circular) answer is that the compiler shouldn’t do that compile-compile optimization because otherwise I can’t do this trick. :) If they break my trick they will also break this https://devblogs.microsoft.com/oldnewthing/20200311-00/?p=103553. – alfC May 10 '21 at 21:03
  • @alfC Yeah, while implementations (probably) can do this, "can" is not the same as "should". However, "shouldn't" is also not the same as "won't", and the great road of C++ is littered by the bodies of people who thought that compilers would never get smart enough to see how they were breaking the rules. – Richard Smith May 11 '21 at 23:22
  • @RichardSmith I agree but sometimes one can trust that things will go in the right direction. Take for example SFINAE, which is very similar to this issue. At first it was not clear if SFINAE was a thing, if it was reliable or even required. It turned out to be so useful that ended up in the language. That is my same opinion for these “unevaluated constant type dependent expressions” (UCTDEs?). Lazy evaluation of template expressions seems to be a fundamental trait of template meta language. Also optimizing this is really a separate issue than runtime optimizations. – alfC May 12 '21 at 04:37
3

The following compiles with clang trunk -std=c++1y:

#include <iostream>

template<int x>
char y = 3;

template<>
double y<42> = 2.5;

char c {y<17>};

double d {y<42>};

So either a specialization of a variable template doesn't need to have the same type as its primary, or clang has a buggy implementation of N3690

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
1

I would fully expect that the declaration of a specialization needs to match the primary template exactly, including its type. This isn't anything new for variable templates. I haven't chased down the details in the standard, yet, to see where it specifies this detail.

The code below seems to do something akin to what you want, i.e., leave the variable type sort of open:

#include <iostream>

template <int X> struct var_type { typedef int type; };
template <> struct var_type<42> { typedef double type; };

int    f(int x) { return x; }
double g()    { return 3.14; }

template <int X>
typename var_type<X>::type var = f(X);
template <>
typename var_type<42>::type var<42> = g();

int main()
{
    std::cout << "var<17>=" << var<17> << '\n';
    std::cout << "var<42>=" << var<42> << '\n';
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Doesn't the example you give contradict your opening claim? The type of `var<17>` is int, and the type of `var<42>` is double. ? So the type of the specialization doesn't match the primary. – Andrew Tomazos Oct 01 '13 at 05:33
  • Incidentally are you aware if gcc or clang has an implementation of variable templates we could play with? – Andrew Tomazos Oct 01 '13 at 05:34
  • @AndrewTomazos: the above code compiles with a recent version of [clang](http://clang.llvm.org/): at the last committee meeting Chandler Carruth and Richard Smith stated that the clang compiler is feature complete with the current C++14 draft when using the `-std=c++1y` with the exception of sized deallocations (but this caveat existed on Friday evening and may be rectified, too). – Dietmar Kühl Oct 01 '13 at 05:40
1

And if so, is it possible to leave the primary undefined somehow?

I think this effectively does it:

template<class T> std::enable_if_t<sizeof(T*)==0> var;
template<> auto var<float >  = 1;
template<> auto var<double>  = 2;

var<char>, for example, seems to be an invalid variable.

For non-type template argument (NTTA) case, it is more tricky I guess, because you need to contemplate all non instantiable cases

template<int x> std::enable_if_t<x!=42 and x!=43> var;
template<> auto var<42>  = 1;
template<> auto var<43>  = 2;

In this case, var<41> is unintanstiable.

I am not sure how robust this is. It works with clang and gcc: https://godbolt.org/z/hxcnjMrce

Finally, may be more readable:

template<class T> auto var  = std::enable_if_t<sizeof(T*)==0>{};
template<int x>   auto var2 = std::enable_if_t<x!=42 and x!=43>{};

EDIT: Actually the NTTA is easier, the only challenge is to make the boolean expression depend on the parameter x.

template<int x> auto var2 = std::enable_if_t<x!=x>{};
template<> auto var2<42>  = 1;
template<> auto var2<43>  = 2;

This is inspired by this other trick: https://devblogs.microsoft.com/oldnewthing/20200311-00/?p=103553

alfC
  • 14,261
  • 4
  • 67
  • 118
0

It's a bit risky to extrapolate that Clang is expressing the feature intended for standardization. (NB: I didn't refer to anything for this answer.)

Of course the fallout from allowing a change of type is that you can't specialize the template after it's been referenced in any way, whereas for all other kinds of templates the cutoff time is at ODR-use. Unless they're planning something wacky, this looks like a Clang bug.

You can always use a type template to declare the type of the variable template.

template< typename t >
struct x_type { typedef … type; };

template< typename t >
typename x_type< t >::type x = …;

Now x_type may be specialized. This just guards against the possibility that Clang is currently buggy. It doesn't allow you to refer to an object of indeterminate type. C++ just doesn't support that. Referring to the object ODR-uses the class template specialization.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • The declaration of a static data member is implicitly instantiated when the containing class type is instantiated, so I think that the variable template specialization case is the same as the class template / static data member case - in terms of requirements on the position of an explicit specialization w.r.t. referencing. – Andrew Tomazos Oct 01 '13 at 07:55
  • @AndrewTomazos Well, a class member's type cannot be changed by an explicit specialization. See 14.7.1/2-3. It's all in terms of "definition is required to exist," a.k.a. ODR-use. `decltype` never instantiates its argument, but this Clang usage can't reconcile `decltype(x)` with subsequent explicit specialization. – Potatoswatter Oct 01 '13 at 23:18
  • I meant explicit specialization of the class template (containing a different declared type for a member), and not an explicit specialization of the class data member definition. (As per the bottom OP example.) Notice that the data member is not a template. The correct mapping is between the class template `S` and variable template `y`. – Andrew Tomazos Oct 02 '13 at 03:51
  • @AndrewTomazos I see now. You're suggesting that the class template analogous to the type is instantiated/used at a different time than the default-value template analogous to the initializer, but that both uses occur independently. Sorry I haven't answered with reference to the standard, but this answer is just saying that I don't think that's how they would have specified it. – Potatoswatter Oct 02 '13 at 05:07
  • Well I searched N3690 for "variable template" and similar, and there are very few references. My rough assumption about this is that for the most part, rules about templates apply equally to class/function/variable templates. That's not very solid though, someone needs to read N3690 haha. :) – Andrew Tomazos Oct 02 '13 at 05:35