I've been referred to "Explicit Template Instantiation" at cplusplus.com, which gives the following example:
template <typename T> class Example
{
public:
Example( T test )
{
_data = test;
}
void setTest(T test)
{
_data = T;
}
private:
T _data;
};
class template Example<int>;
class template Example<float>;
class template Example<double>;
Apart from what looks like an omission error to me where a type is attempted to be assigned to a member variable -- _data = T instead of what I assume should be _data = test -- what I don't understand is what do the last 3 lines declare or instruct the compiler to do, exactly?
I know what templates are, have built programs with them, and know in general about their instantiation and specialization. I do probably have some holes in the understanding of the latter two, but I typically instruct an explicit template instantiation using e.g. template class Example<int>; form and not the one shown in the snippet.
I've tried to compile the snippet using g++ -std=c++11 -pedantic and it compiles just fine and without warnings (I corrected the _date = T error above first).
This came after I commented on an answer to a related question and I am still unsure whether either of the last 3 lines in the snippet is a template specialization or instantiation.
I have also tried to locate the relevant grammar production rule (one allowing template after class) in the C++11 draft published by ISO but came empty handed.
We can see from the following godbolt example this is ill-formed according to clang and MSVC and looking at the draft standard section on Explicit instantiation section [temp.explicit] I don't see any justification for gcc to accepts it.
I believe what the article "possibly" meant given the topic was:
template class Example<int>;
template class Example<float>;
template class Example<double>;
and that indeed is well-formed with gcc/clang/MSVC.
It looks like pre C++11 this grammar was allowed, see defect report 1707: template in elaborated-type-specifier without nested-name-specifier (emphasis mine):
The grammar for elaborated-type-specifier in 10.1.7.3 [dcl.type.elab]
reads, in part,
elaborated-type-specifier:
class-key nested-name-specifieropt templateopt simple-template-id
This allows use of the template keyword without a
nested-name-specifier, e.g., struct template S. This is
inconsistent with other uses of the template keyword. It might be
better to split the production in two and only allow the keyword
following a nested-name-specifier,
....
So this makes a little more sense with this comment that -ansi causes a warning.
The other answerer filed two bug reports.
cppreference has a good dicssuion of Explicit instantiation and this SO question Explicit instantiation - when is it used? explains more details why this is useful.
Also note, we can see this Meta post: Links being changed to cppreference.com that the site has been known to have incorrect information and in general the community prefers cppreference as a solid C++ reference.
I see two bugs here:
GCC treats the template keyword here as the template disambiguator, and thus believes that class template Example<int> is equivalent to class Example<int>. This is incorrect because the C++ grammar only permits the template disambiguator to be after ::, . or ->. (C++11 as originally written allows class template Example<int>, but this has been fixed by cwg 1707.)
GCC incorrectly allows a declaration like class Example<int>;. Although class Example<int>; matches the grammar of simple-declaration, it fails to meet the requirement in [dcl.dcl]/5 which states that a simple-declaration must declare or redeclare something (class/enumeration/enumerator/typedef/variable/function).
The former has been reported as GCC bug 87781, the latter as GCC bug 87783.
Update: GCC bug 87781 is now fixed by r266285.
Related
I started experimenting with the C++20 feature of concepts and was very pleased when I realized that it is possible to partially explicitly provide template arguments for concepts. I read the cppreference article and did not find that mentioned there.
But then I realized something strange: the order of specification of template arguments is reversed to what I would have expected. When providing one explicite template argument, it replaces the second template in the template list:
#include <concepts>
#include <type_traits>
/// Concept in order to deduce if sth. is base of sth else
template <typename Impl, typename Base> //XXX: here the order of Impl and Base are not
concept Implements = std::is_base_of_v<std::remove_reference_t<Base>, // what I would've expected.
std::remove_reference_t<Impl>>;
/// Example Base class impl
struct BaseExample {};
/// Implementation of BaseExample
struct ImplExample : BaseExample {};
/// Function in which the concept is applied
template <Implements<BaseExample>... Baes> void f(Baes &&... ) {}//} ((void)b, ...); }
int main() {
(void) std::is_base_of_v<BaseExample, std::remove_reference_t<ImplExample &&>>; //< true
(void) std::is_base_of_v<BaseExample, std::remove_reference_t<ImplExample&>>; //< true
f(ImplExample{}, ImplExample{});
}
From my point of view the possibility to partially provide explicit template arguments makes sense, as the argument against partial template specification for classes do not apply here and make concepts more general. Now I wonder:
Will partial explicit template specifications (likely) be allowed when the standard is released?
Will this order of specifications likely stay the same or is this a bug?
How would I answer this question for myself? From what I understand the c++20 standard is not ready by now and I found a list of C++ Standard Committee Papers, of which I briefly searched the headlines of the ones proposed in 2020 for 'concept'. Is checking these papers the way to go, or is there an accessible single document which combines the points the authors currently agreed upon?
The code can be found here.
edit
After posting this I checked the behavior when three template arguments are specified. It looks like I misinterpreted the specification order: The first argument is 'held free' to contain the argument to be checked, and the explicit specifications start with the second argument. This can be seen here.
Even though I figured out the reasoning behind the order of specification I would be very interested in the answers to questions above.
Yes, partial-concept-ids are surely a C++20 thing. The special status of the first argument, while surprising, allows cases like std::constructible_from which is declared as
template<class T,class ...Args>
concept constructible_from=…;
std::constructible_from<int,int> is a type-constraint that requires that whatever it introduces be constructible from two int arguments. However, it can also be an expression, in which case it reports whether an int can be constructed from an int (spoilers: true), but that potential confusion exists regardless of the argument order.
If T had to go at the end, there would be no way of using such a concept: only template argument deduction or default template arguments can supply values for a template parameter beyond a parameter pack, and neither of those applies here.
Every mailing posted at the papers site you linked includes the latest draft of the standard, and alternate mailings include annotations as to what papers were adopted. Or you can just visit the draft’s repository (at least if you’re happy reading LaTeX).
In this example:
template<class T>
struct S : T
{
using T::X;
};
T::X is a dependent name that refers to the member X in T.
If S<T> is instantiated with T = X:
struct X
{
X(int) {}
};
...
S<X> s(42);
Will the using-declaration become inheriting-constructor?
Clang rejects the code DEMO, while g++ accepts it.
Note that if we write:
using T::X::X;
Both compilers accept the code and treat it as inheriting-constructor.
Is using T::X allowed to become inheriting-constructor by standard?
Thanks to T.C. for pointing this out:
Core issue 2070, which is in the drafting stage (it's confirmed to be a problem, and the wording of the solution is being worked on), deals with this case. The proposed fix is requiring both IDs to be the same ID in order for this code to be accepted as an inheriting constructor.
In this light, the error message from clang makes sense, as T::X would be the type X, which triggers the "type from template without typename tag" error.
Original Post:
So it seems to me that the real question is, "Is it allowable that a template instantiation changes the semantic meaning of a using statement?"
And the answer is, it is not disallowed. I do not know if this interaction was foreseen and intended by the authors of the standard. But as far as I can see, referencing both the using declaration in Section 10 and the template initialization in Section 17, by the letter of the standard, yes, using T::X is allowed and yes, the using declaration will become an inheriting constructor when T is X.
I am writing a cross-platform application in two compilers (Clang on Xcode v5.0.2, and Visual Studio 2012 Update 4) and I have run into a scenario in which the two compilers disagree about the required syntax for the use of the template keyword in a nested declaration.
Here is the code (boiled down to an easily reproducible test case):
template<typename T>
struct Base
{
template<typename U>
struct InnerBase
{};
};
template<typename T, typename U>
struct Derived : public Base<T>
{
// the "template" keyword is REQUIRED in Clang/OSX
struct InnerDerived : public Base<T>::template InnerBase<U>
{};
// the "template" keyword is FORBIDDEN in Visual Studio 2012
struct InnerDerived : public Base<T>::InnerBase<U>
{};
};
int main()
{
Derived<int, int>::InnerDerived foo;
}
As noted, the two compilers disagree about the use of the "template" keyword.
For Clang, when the template keyword is not included, the error is:
Use 'template' keyword to treat 'InnerBase' as a dependent template
name
For Visual Studio, when the template keyword is included, the error is:
'Base::InnerBase' : use of class template requires template
argument list
I have looked at various other StackOverflow questions regarding the rules for use of the template keyword (for example, Where and why do I have to put the "template" and "typename" keywords?). However, looking at this, and other similar questions, does not give me confidence in claiming that one compiler is correctly implementing C++11 and that the other is not.
(Note that Clang's error makes sense to me, while the VS error doesn't make much sense to me because it seems that I am including the template argument list.)
Which compiler is correct in this case? Should the template keyword be included, or not, in the sample code above (for C++11 compliance)?
(Possibly I have not set the compiler settings correctly to use C++11 in one or the other case - in which case, my question still stands: which version of the code above is correct C++11 code?)
It seems the relevant clause is 14.2 (temp.names) paragraph 4:
When the name of a member template specialization appears after . or ->in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template.
I think that says that template is required. Following up with DyP's comment, it seems certainly wrong to reject the keyword even if it isn't required (paragraph 5 of the same clause):
A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template. [Note: The keyword template may not be applied to non-template members of class templates. —end note] [Note: As is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or . is not dependent on a template-parameter, or the use does not appear in the scope of a template.—end note].
Clang is correct, Base<T> is dependent on a template parameter. This is yet another symptom of Visual C++ not implementing two-phase name lookup for templates.
Recently, I was reading the book: C++ templates: the complete guide written by David Vandevoorde and Nicolai M. Josuttis.
Specifically about template parsing quoting from the book pp 126.
Class templates also have injected class names, However, they are stranger than ordinary injected class names: They can be followed by template arguments (in which case they are injected class template names ), but if they are not followed by template arguments they represent the class with its parameters as its arguments (or, for a partial specialization, its specialization arguments).
The relevant codes are excepted from the book as follows:
template<template<typename> class TT>
class X
{
};
template <typename T>
class C
{
C* a; //OK, same as "C<T>* a"
C<void> b; // OK
X<C> c; //Error, C without a template argument list does not denote a template
X< ::C>d;
};
int main()
{
return 0;
}
The above code example tries to explain the whole quoted paragraph.
I compiled the above code in gcc 4.5.3, it outputs:
error: field ‘b’ has incomplete type
Therefore, I have the following questions:
Why the compiler generates totally different error messages? the book says b is OK, but gcc gave the error; meanwhile, other errors listed in the book are not detected? Why, is this a possible compiler bug or error in book?
What does injected class names mean? How can I identify what names are injected class names and what are not?
why C*a is the same as C<T>* a? I tried to replace C*a with C<T>* a, no error is reported, so is C* a a shorthand for C<T>* a?
Thanks a lot!
Note: An injected-class-name is just the identifier used to declare the class (as opposed to other names which also refer to the same class, such as typedef names).
Here's the relevant quote from the C++11 Standard, section 14.6.1p1:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.
So clearly this is legal in C++11.
However, the book correctly describes the behavior of C++03:
Like normal (non-template) classes, class templates have an injected-class-name (clause 9). The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.
Aside from reversing the logic, we see that C++03 contained one case where the identifier referred to the template (when template arguments were provided) and C++11 adds two additional cases, one of which affects this code.
So in C++03, the code was equivalent to X<C<T>> c;, which is an error because X needs to be passed a template and not a type.
Bottom line: For learning about the language, templates especially, you need a book on C++11. Older books remain useful with respect to architecture and high level design, but can't explain language intricacies which changed after they were published.
Trying to answer your questions:
Different compilers across vendors and versions generate different diagnostics. You cannot rely on exact messages from one to another.
"injected class name" is defined in the standard under §9, paragraph 2:
A class-name is inserted into the scope in which it is declared
immediately after the class-name is seen. The class-name is also
inserted into the scope of the class itself; this is known as the
injected-class-name. For purposes of access checking, the
injected-class-name is treated as if it were a public member name. A
class-specifier is commonly referred to as a class definition. A class
is considered defined after the closing brace of its class-specifier
has been seen even though its member functions are in general not yet
defined. The optional attribute-specifier-seq appertains to the class;
the attributes in the attribute-specifier-seq are thereafter
considered attributes of the class whenever it is named.
The reason C* a is the same as C<T>* a is due to context: both occur within the declaration of C. C++ allows a "shortcut" in that a templated class can be refer it's own instantiation without the template arguments within a context in which the arguments can be assumed (this is one of them). If you wanted to refer to an instantiation of C with different template arguments, you would need to be explicit about it.
The reason you're getting your particular error is that C<void> is an incomplete type at the time at which you're trying to instantiate a C with different template arguments.
What I find really odd, looking at the code, is that I think it should compile cleanly because you're never actually instantiating either template, so type completeness of C shouldn't matter.
I think the book forgot * there
C<void> *b;
Otherwise the declaration is not valid even with out templates. Because C is not completely declared yet when the compiler reaches that line. It is the same as trying to use a forward declare class.
For example this is not valid.
class A {
A a;
^// A has incomplete type here
};
However this is valid.
class A {
A *a;
};
Injected class name is when the template argument of a class is injected with in the class's own scope to its uses.
tempalte<typename T>
class C {
C* a; // same as C<T>* a;
};
I think the book explains it completely.
so is C* a a shorthand for C<T>* a?
Only within the class's own scope. (including member definition scopes) e.g.
template <typename T>
void C<T>::f() {
C a; // Same as C<T> a;
}
Why the compiler generates totally different error messages? the book
says b is OK, but gcc gave the error; meanwhile, other errors listed
in the book are not detected? Why, is this a possible compiler bug or
error in book?
Apart from the things explained above, the reason can be that the book is relatively old and compilers have changed since then. You can't expect error messages to be exactly the same.
From the book - C++ Templates: The Complete Guide by David, Nicolai
Thus, templates are compiled twice:
Without instantiation, the template code itself is checked for correct syntax. Syntax errors are discovered, such as missing
semicolons.
At the time of instantiation, the template code is checked to ensure that all calls are valid. Invalid calls are discovered, such as
unsupported function calls.
Keeping the first point, I wrote -
template<typename T>
void foo( T x)
{
some illegal text
}
int main()
{
return 0;
}
It build fine on Visual Studio 2010 with out any warnings with optimizations turned off. How ever, it failed on gcc-4.3.4. Which one is complying to the C++ standard ? Is it mandatory for template code to get compiled even with out template instantiation ?
The program in question is ill-formed, but the C++ standard does not require a diagnostic in this case, so both Visual Studio and GCC are behaving in a compliant fashion. From §14.6/7 of the C++03 standard (emphasis mine):
Knowing which names are type names allows the syntax of every template definition to be checked. No
diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no
valid specialization can be generated for a template definition, and that template is not instantiated, the template
definition is ill-formed, no diagnostic required. If 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 done, and
if the completeness of that type affects whether or not the program is well-formed or affects the semantics
of the program, the program is ill-formed; no diagnostic is required. [Note: if a template is instantiated,
errors will be diagnosed according to the other rules in this Standard. Exactly when these errors are diagnosed
is a quality of implementation issue. ] [Example:
int j;
template<class T> class X {
// ...
void f(T t, int i, char* p)
{
t = i; // diagnosed if X::f is instantiated
// and the assignment to t is an error
p = i; // may be diagnosed even if X::f is
// not instantiated
p = j; // may be diagnosed even if X::f is
// not instantiated
}
void g(T t) {
+; //may be diagnosed even if X::g is
// not instantiated
}
};
—end example]
The book you're looking at seems to reflect (mostly) that author's observations about how compilers really work, not the requirement(s) of the standard. The standard doesn't really say much to give extra leniency to a compiler about ill-formed code inside a template, just because it's not instantiated.
At the same time, the book is correct that there are some things a compiler really can't do much about checking until it's instantiated. For example, you might use a dependent name as the name of a function (or invoke it like a function, anyway -- if it's functor instead that would be fine too). If you instantiate that template over a class where it is a function, fine and well. If you instantiate it over a class where it's really an int, attempting to call it will undoubtedly fail. Until you've instantiated it, the compiler can't tell which is which though.
This is a large part of what concepts were really intended to add to C++. You could directly specify (for example) that template X will invoke T::y like a function. Then the compiler could compare the content of the template with the declarations in the concept, and determine whether the body of the template fit with the declaration in the concept or not. In the other direction, the compiler only needed to compare a class (or whatever) to the concept to determine whether instantiating that template would work. If it wasn't going to work, it could report the error directly as a violation of the relevant concept (as it is now, it tries to instantiate the template, and you frequently get some strange error message that indicates the real problem poorly, if at all).