Why does the Standard prohibit friend declarations of partial specializations? - c++

The C++ standard prohibits friend declarations of partial specializations. (§14.5.3/8):
Friend declarations shall not declare partial specializations. [Example:
template<class T> class A { };
class X {
template <class T> friend class A<T*>; //error
};
--end example]
Other questions, e.g. this one,
have received answers that invoke this prohibition, but I would like to know the
rationale. I don't see it and can't find it with my favourite search engine. I
can find however that it goes right back to the C++98 standard, so presumably
the rationale is quite basic and clear. Can someone explain it to me?

I don't have a reference but I suspect that this is because it would result in the partial specialization being declared in the scope of the friend-declaring class rather than the scope of the template in question, and rather than creating a bunch of rules to force the friend declaration to result in the specialization being in the correct scope, they simply prohibit it.

Here is some undirect explanation:
http://www.cprogramming.com/tutorial/template_specialization.html
A final implementation detail comes up with partial specializations:
how does the compiler pick which specialization to use if there are a
combination of completely generic types, some partial specializations,
and maybe even some full specializations? The general rule of thumb is
that the compiler will pick the most specific template
specialization--the most specific template specialization is the one
whose template arguments would be accepted by the other template
declarations, but which would not accept all possible arguments that
other templates with the same name would accept.
I infer that maybe it is not permitted to prevent any ambiguity in the determination of specialization type.

Related

Detect if a type is a specialization from the primary template or a user-provided specialization

Let's say I have this:
template<typename T>
class my_template {...};
Now, users are expected to be able to specialize my_template for their own types. And they will pass those types to some API functions of mine, which will use properties of my_template<T> to do stuff.
So at some point in my code, I have a my_template<T>. I want a meta-function of some kind which takes my_template<T> and results in a compile-time value of true if my_template<T> is a user-provided specialization (partial or explicit) and false if it isn't.
The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
This question is not sophistry. The C++20 specification has a meta-function ITER_CONCEPT(I), which has different internal behavior based on whether std::iterator_traits<I> is from the primary template or a user-provided specialization. Of course, being the standard library, they can create an identifier prefixed with __ as a member of the primary template and thereby declare any attempted forgery from user-space to be undefined behavior.
Is this a thing only a compiler/standard library implementation can do in a foolproof, or is it possible to do this in C++20?
The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.
That's basically it, yep. For instance, libstdc++'s iterator traits has its primary class template inherit from a hidden base class template, and then checks for inheritance from that base.
Yes, a user could forge an explicit specialization by providing an appropriate definition - but, like, don't. That isn't something you would do by accident, that's explicitly and pointlessly malicious, and the typical saying is that the library and the language defend against Murphy, not Machiavelli.
With Modules, you can make it even harder for the user to be explicitly malicious by exporting the primary template but not actually exporting the base class that you're using to check if the class template was specialized:
export module std;
namespace std {
template <typename T> struct __iterator_traits { };
template <typename T>
export struct iterator_traits : __iterator_traits { };
}
Now the user can't even name std::__iterator_traits in order to break the library. Perhaps reflection will still give them a way to do so (assuming those things are still accessible), but now they'd really be jumping through a lot of hoops.
Speaking of Reflection, part of the currently-specified API (P1240) includes the functions is_specialization, is_partial_specialization, and is_explicit_specialization. If those mean what I think they mean, then when we eventually get reflection, then the library wouldn't have do use these magic base / magic member hacks and could just directly check to see if the provided specialization was a partial or explicit specialization or not. I figure the library should probably add is_primary_specialization for completeness.

Can std::initializer_list be specialized?

While going through the various rules on list-initialization, I found this in dcl.init.list#3.6:
Otherwise, if T is a specialization of std​::​initializer_­list<E>, the object is constructed as described below.
On the other hand, in the synopsis of std::initializer_list, in support.initlist, I found the following statement:
If an explicit specialization or partial specialization of initializer_­list is declared, the program is ill-formed.
These appear to be contradictory statements, so what am I misunderstanding?
"A template specialization" has two distinct meanings:
"Explicit (full) specialization" or "partial specialization" - a language construct that changes the meaning of a template for some combination of template parameters.
Something that was generated from a template by substituting template arguments into it.
In other words, if you specify template arguments for a template, the resulting type/function/variable/... is a specialization of that template. E.g. std::vector<int> is a specialization of std::vector.
Looks like the first passage you quote uses (2).
So "if T is a specialization of std​::​initializer_­list<E>" roughly means "if there exists such E that std::is_same_v<T, std::initializer_list<E>>", or "if T is a std::initializer_list<E>".
There is no contradiction.
If an explicit specialization or partial specialization of initializer_­list is declared, the program is ill-formed.
Means you cannot declare a specialization. The compiler itself is allowed to stamp out specializations of std​::​initializer_­list
The thing that might be causing you issue is that the concrete type you get out of a template is called a specialization. This is what the first paragraph is talking about. The second paragraph is talking about actually defining/declaring a specialization for std::initializer_list

extending namespace std via partial template specialization

As far as I know, we are allowed (with some exceptions that I won't mention here) to "extend" namespace std by totally specializing a std template function such as std::swap, i.e.
namespace std
{
template<>
void swap<Foo>(Foo& lhs, Foo& rhs){...}
}
is perfectly valid.
Since C++11, we can now partially specialize functions. I believe that we can then play the same game and extend std via partial specialization, like
namespace std
{
template<typename T>
void swap<Foo<T>>(Foo<T>& lhs, Foo<T>& rhs){...}
}
however I am not sure about this and couldn't find the proper explaining section in the standard. Is the code immediately above correct or does it lead to UB?
PS: As #Columbo mentioned in the answer, we cannot partially specialize function templates, not even in C++11/14. For some reason I thought one can do that, I believed it was at least a proposal.
You might mean [namespace.std]/1:
A program may add a template specialization for any standard library
template to namespace std only if the declaration depends on a
user-defined type and the specialization meets the standard library
requirements for the original template and is not explicitly
prohibited181.
181) Any library code that instantiates other library templates must be prepared to work adequately with any user-supplied
specialization that meets the minimum requirements of the Standard.
If partial specializations of function templates are ever introduced, this quote would also implicitly cover them (as it doesn't restrict itself on explicit specialization).

C++ template explicit specialization - calling existing member function

I'm using explicit template specialization to initialize a std::vector with information but only for a specific type of std::vector, thus the explicit specialization. Within the constructor, if I try to call push_back or any other existing function in std::vector, compilation fails. What is the problem and how do I fix it?
simplified example:
namespace std
{
template<>
class vector<int>
{
public:
vector(void)
{
int value = 5;
push_back(value);
}
};
}
compiler message:
In constructor 'std::vector<int>::vector()':
error: 'push_back' was not declared in this scope
push_back(value);
^
Explicit specializations are completely different classes that are separate from the primary template. You have to rewrite everything.
In normal situations where you control the primary template, you would typically have some sort of common base class or base class template to collect common structures.
With a given library, it is generally a very bad idea to add specializations (unless the library explicitly says it's OK). With the C++ standard library, this is outright undefined behaviour.
(The main problem is that other translation units may be using the template instantiation which you're specializing without seeing your specialization, which violates the one-definition rule.)
Template specializations are unrelated types from both the primary template and any other specialization. It is unclear what you are attempting to do, as it is also illegal to provide specializations of templates in the std namespace unless the specialization uses your own user defined type.
If you can explain the problem to solve, you might get other options, like specializing a member function rather than the template itself...

Template-like declaration for function overloads

Can I make generic function declarations at the start of my header file?
I can do template<class t> t func(t); then specialize it, but
Template Specialization VS Function Overloading
says not to do that.
First, you can surely declare a template function and then define it, and/or define specializations. But...
Function specializations must be full specializations, that is, you cannot partially specialize a template function. Now, while you can actually specialize the function template, providing overload may have advantages (and disadvantages), but in most cases it will be a better option.
You might want to read this: http://www.gotw.ca/publications/mill17.htm