Can std::initializer_list be specialized? - c++

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

Related

Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3

[temp.arg.explicit]/3 of the C++17 standard (final draft) says about deduction of function template arguments with explicitly specified template argument lists:
In contexts where deduction is done and fails, or [...], if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
How does this apply to parameter packs?
Consider
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
This compiles on MSVC, but not on GCC and Clang, see godbolt. It would also be my intuition that it should fail, because deduction will fail, but the quote above seems to imply that even if deduction fails, since f<int> (in my understanding) identifies uniquely a template specialization, f<int> should be considered to refer to that specialization and then call it, without overload resolution, which will work, implicitly converting 0 to S<int>.
What is wrong in my understanding of the quote or is MSVC indeed correct?
Note that if we try to call f<>(0); (which I guess should work by the considerations above) all three compilers refuse to compile.
Relevant for the question is also [temp.arg.explicit]/6 that tells us that implicit conversions on a function parameter (as you want above) are
if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]
So, now is the question if A... participates in template argument deduction. (At this point I wanto to note that OPs code compiles also under gcc/clang if we replace the parameter pack by one template parameter, as it should since it is explicitly specified).
One could argue that A... is explicitly specified and therefore does not participate in deduction. But I would argue that one is wrong. [temp.arg.explicit]/9 tells us that deduction can extend explicitly specified template argument lists. Hence, f<int>(S<int, char>{0}); is valid and A... is deduced to int, char. So in this case A... definitely participates in deduction. But since this call only differs from your call by the parameter, the deduction also has to take place in your call, too.
In other words f<int>(0); could also mean to call f<int, char> and as such, it does not specify a single function template specification.
It's irrelevant. There's no such thing as a function call "without overload resolution". CWG2092 makes this clear.
[temp.over]/1 controls (broken apart for readability; emphasis mine):
When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments.
For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution.
If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template. The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].
There is an open core language issue (issue 2055: Explicitly-specified non-deduced parameter packs) that is well related to this case.
From my understanding of what is implictly stated in this issue, the intent is that compilers should behave as MSVC but the standard is said not to be clear enough.

Dependent non-type parameter packs: what does the standard say?

I think the following code is well-formed:
template< typename T >
using IsSigned = std::enable_if_t< std::is_signed_v< T > >;
template< typename T, IsSigned< T >... >
T myAbs( T val );
Others say that it is ill-formed, because §17.7 (8.3) of the C++17 standard:
Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if: (...) every valid specialization of a variadic template requires an empty template parameter pack, or (...)
In my opinion IsSigned< T >... is a dependent template parameter, therefore it can not be checked against §17.7 (8.3) in template definition time. IsSigned< T > could be for example void for one subset of Ts, int for another subset or substitution failure. For the void subset it is true, that the empty template parameter pack would be the only valid specialization, but the int subset could have many valid specializations. It depends on the actual T argument.
It means that the compiler must check it after the template instantiation, because T is not known before. At that point the full argument list is known, there is zero variadic arguments. The standard says the following (§17.6.3 (7)):
When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct
This is why I think it is well formed.
What do you think?
How can I track down this ambiguity for sure? It is hard to decide, because the code compiles but it means nothing: §17.7 (8.3) is NDR, the compilers do not have to raise any compilation error.
The code is ill-formed, no diagnostic is required.
If std::is_signed_v<T>, then std::enable_if_t<std::is_signed_v<T>> denotes the type void. Otherwise, std::enable_if_t<std::is_signed_v<T>> does not denote a valid type. Therefore, every valid specialization of myAbs requires an empty template parameter pack.
Per [meta.rqmts]/4, the program has undefined behavior if std::enable_if is specialized. Therefore, the aforementioned behavior cannot be changed.
In my opinion IsSigned< T >... is a dependent template parameter,
therefore it can not be checked against §17.7 (8.3) in template
definition time. IsSigned< T > could be for example void for one
subset of Ts, int for another subset or substitution failure. For
the void subset it is true, that the empty template parameter pack
would be the only valid specialization, but the int subset could
have many valid specializations. It depends on the actual T
argument.
The compiler cannot check it, in the same way it cannot, say, solve an arbitrary equation for you. NDR (no diagnostic required) is made exactly for such cases — the program is ill-formed and would require a diagnostic if the compiler is actually capable of detecting that. NDR permits the compiler not to check it.
When N is zero, the instantiation of the expansion produces an empty
list. Such an instantiation does not alter the syntactic
interpretation of the enclosing construct.
The rule we are talking about is a semantic rule, not a syntactic rule, because syntactic rules are in [gram].
So what is the rationale for the NDR rules? In general, they address problems that are not reproducible among implementation strategies. For example, they may cause the code to misbehave in some implementation strategies, but do not cause any problems (and cannot be easily) in others.
Also, note that the standard talks in terms of program with terms like "ill-formed". Therefore, it is not always plausible to talk about the well-formed ness of an isolated code snippet. In this case, std::enable_if is required not to be specialized, but the situation may get more complicated otherwise.

template with lambda as unique default parameter on each instantiation

I'm looking for a way to automatically make default template parameter be unique each time a template is instantiated. Since unnamed function objects created by lambda expressions have different types I thought of adopting them somehow. With recent changes to standard daft removing "A lambda-expression shall not appear in ... a template-argument" restriction (see Wording for lambdas in unevaluated contexts) it seemed like a good idea. So I wrote the following kinda working snippet that compiles on recent gcc and clang:
#include <type_traits>
template<void ( * ) (void) = [](){}> class
unique final {};
static_assert(false == ::std::is_same_v<unique<>, unique<>>);
int main()
{
return 0;
}
Is this a viable approach or one of those "ill-formed, no diagnostic is required" cases?
Some additional context: I want to use this to implement Ada-style strong type definitions that should work in a single translation unit without manually inventing unique tags that would be otherwise unused:
struct _tag_WowInt {};
using Int = type<int, _tag_WowInt>;
struct _tag_SoUnique {};
using DifferentInt = type<int, _tag_SoUnique>;
Upd1: I would like to mention that approaches involving __COUNTER__ or similar macros won't work in general case because they will be expanded by preprocessor only once and won't yield unique types when used inside of template for example.
I believe that you are right, it seems to me that is "ill-formed, no diagnostic required". I think this is covered by
[temp.res/8.4] and [temp.res/8.5]:
(8.4) ― a hypothetical instantiation of a template immediately following its definition would be ill-formed due to
a construct that does not depend on a template parameter, or
(8.5) ― the interpretation of such a construct in the hypothetical instantiation is different from the interpretation
of the corresponding construct in any actual instantiation of the template. [Note: This can happen in situations including the following:
(8.5.1) ― a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is performed, or
(8.5.2) ― lookup for a name in the template definition found a using-declaration, but the lookup in the corresponding scope in the instantiation does not find any declarations because the using-declaration was a pack expansion and the corresponding pack is empty, or
(8.5.3) ― an instantiation uses a default argument or default template argument that had not been defined at the point at which the template was defined, or
(8.5.4) ― constant expression evaluation within the template instantiation uses
(8.5.4.1) ― the value of a const object of integral or unscoped enumeration type or
(8.5.4.2) ― the value of a constexpr object or
(8.5.4.3) ― the value of a reference or
(8.5.4.4) ― the definition of a constexpr function,
and that entity was not defined when the template was defined, or
(8.5.5) ― a class template specialization or variable template specialization that is specified by a non-dependent simple-template-id is used by the template, and either it is instantiated from a partial specialization that was not defined when the template was defined or it names an explicit specialization that was not declared when the template was defined.
— end note]
Even though your use case is not explicitly listed in the examples of the note, in my understanding the requirement implies that unique<> must refer to the same thing throughout the whole program, otherwise it is ill-formed, no diagnostic required.
This was CWG1850. The Committee appear to dislike this
kind of stateful meta-programming. The constexpr counter no longer works
in newer versions of the compilers.

Implicit instantiation of specialization

I can't understand why the standard first defines template instantiation for templates as follows N3797::14.7/4 [temp.spec]:
The act of instantiating a function, a class, a member of a class
template or a member template is referred to as template
instantiation.
But further everywhere it uses the instatiation of the specialization, like the following N3797::14.7/4 [temp.spec]:
An instantiated template specialization can be either implicitly
instantiated (14.7.1) for a given argument list or be explicitly
instantiated (14.7.2).
I don't understand that. A template itself is a different concept than the template specilization, which could be an explicit specialization or a partial specialization. For instance N3797::14.5.5/1 [temp.class.spec]:
The primary template shall be declared before any specializations of
that template.
My question is about why the Standard first declare the instatiation concept for templates, but further it applies that concept for template specializations?
Moreover N3797::14.7/4 [temp.spec] defines the specialization concept as follows:
A specialization is a class, function, or class member that is either
instantiated or explicitly specialized (14.7.3).
So, the partial specialization is not a specialization, is it? I'm totally confused by those concepts. Couldn't you clarify it a bit?
This is a similar question here.
And now,I try to answer this question again.
A template is a type of infinite, so we can not instantiate a template, we can only be instantiated template specialization.
Implicitly instantiated, the current compilation unit requires the use of the template code, the compiler automatically instantiated template specialization.
Explicitly instantiated, we manually enter the code causes the compiler to instantiate a template specialization.
explicit specialization, given all the template parameters, and gives a non-generic code. Once the template type match, then instantiate this specialization.
Partial specialization, some parameters are given template and gives a non-generic code. Once the template type match, then instantiate this specialization.

Why does the Standard prohibit friend declarations of partial specializations?

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.