I've seen the following pre-C++11 code, used as a trick to declare class template friends (which in C++11 can simply be done with friend T;)
template <typename T>
struct Wrapper
{
typedef T type;
};
template <typename T>
class Foo
{
friend class Wrapper<T>::type; // effectively makes T a friend
};
struct Test{};
int main()
{
Foo<Test> foo;
}
The code compiles fine on g++ (4.9/5.1/6), but fails under clang++ (3.5/3.6/3.7) with the error
error: elaborated type refers to a typedef
friend class Wrapper::type;
Is the code above standard compliant, i.e. valid or not?
§7.1.6.3/2:
If the identifier resolves to a typedef-name or the
simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.
It's not compliant. The grammar rules for friend in [class.friend]/3 are:
A friend declaration that does not declare a function shall have one of the following forms:
friend elaborated-type-specifier ;
friend simple-type-specifier ;
friend typename-specifier ;
class Wrapper<T>::type is none of those specifier types. It's not an elaborated-type-specifier because Wrapper<T>::type isn't an identifier or a class-name, and obviously isn't one of the other two either. What you're looking for is simply:
friend typename Wrapper<T>::type;
[dcl.typedef]/p8:
[ Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (9.1)
If a typedef-name is used to identify the subject of an elaborated-type-specifier (7.1.6.3), a class definition
(Clause 9), a constructor declaration (12.1), or a destructor declaration (12.4), the program is ill-formed.
— end note ] [Example:
struct S {
S();
~S();
};
typedef struct S T;
S a = T(); // OK
struct T * p; // error
— end example ]
The code should fail at template instantiation time, which it does so correctly in Clang.
Using typename in place of struct allows the code to pass in both compilers.
Related
I've stumbled upon an error which manifestates itself only on GCC 6.2.0, but not on Clang 3.9.0 (both in -std=c++14 mode). I'm unsure which behavior is correct (and whether I should file a bug).
Here's the code:
template<typename type_t>
class foo_t
{
};
class bar_t
{
public:
using foo_t = int;
};
class baz_t:
public bar_t
{
private:
template<typename type_t>
friend class foo_t;
};
On GCC this gives the following error:
test.cpp:17:15: error: using typedef-name ‘using foo_t = int’ after ‘class’
friend class foo_t;
^~~~~
test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here
using foo_t = int;
^
From what I know of C++ standard, parent typedef's (or usings) should not leak into scope of the child and you need to explicitly qualify the name: see for example Propagating 'typedef' from based to derived class for 'template'. So it seems to me that GCC is incorrect here, but I'm not so sure of my C++ knowledge to say with confidence.
Thanks for any help!
From what I know of C++ standard, parent typedef's (or usings) should not leak into scope of the child and you need to explicitly qualify the name
That is incorrect. Members (including type aliases) declared in base classes are visible in derived classes normally. The question you linked to specifically deals with templates with a dependent base class, where two-phase name lookup applies (and again, applies to everything, not just type aliases).
This aside, the relevant part of the standard is C++14 (N4140) [dcl.type.elab] 7.1.6.3/2 (emphasis mine):
3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier
resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the
same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or
the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.
(Note: elaborated-type-specifier is the construct class T for some T that we're dealing with).
3.4.4, in turn, says that when resolving the identifier in an elaborated-type-specifier into a name, non-type names are ignored (but type names are found normally).
GCC is therefore actually right, since the typedef-name foo_t in scope bar_t is "closer" in scope than the global-scope template-name foo_t. The unqualified name foo_t inside baz_t therefore resolves to bar_t::foo_t, which is a typedef-name and therefore makes the elaborated-type-specifier ill-formed.
The problem is with the resolution of the unqualified name foo_t. As you note yourself in comments to the question, explicitly stating which foo_t you mean should solve the issue:
tempalte <typename type_t>
friend class ::foo_t;
Members' declaration DO visible to its subclass. The problem for your question is about duplicate declaration. Please check the following code
template<typename type_t>
class foo_t
{
};
class bar_t
{
public:
using foo_t = int;
};
class baz_t :
public bar_t
{
public:
foo_t val; // Here, the foo_t refers to the int
};
int main()
{
baz_t().val; // this works
}
The error code you post test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here already mentioned this problem.
On the other hands, friend class cannot be applied to simple data type, please check the following code:
using foo_t = int;
class bar_t
{
public:
};
class baz_t :
public bar_t
{
public:
template<typename type_t>
friend class foo_t;
//error: using typedef-name ‘using foo_t = int’ after ‘class’
};
Here is the workaround if you really need to have the duplicated name of declaration:
template<typename type_t>
class foo_t
{
};
class bar_t
{
public:
using foo_t = int;
};
class baz_t:
public bar_t
{
public:
template<typename type_t>
friend class ::foo_t; // The global scope
foo_t val; // The int
};
The following quote is from 14.5.1/4 [temp.class]:
In a redeclaration, partial specialization, explicit specialization or
explicit instantiation of a class template, the class-key shall agree
in kind with the original class template declaration
I thought it meant that we cannot declare an explicit specialization with another class key, for instance:
template <class T, class W>
struct A
{
void foo();
};
template <class T, class W>
class A<T*, W> // Should have printed an error
{
void foo();
};
DEMO
But it works fine. So what's the point of that rule?
Right after the quoted sentence is a reference to [dcl.type.elab]. [dcl.type.elab]/p3 describes what "agree in kind" means:
The class-key or enum keyword present in the
elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. [...]
Thus, in any elaborated-type-specifier, the enum keyword shall be
used to refer to an enumeration (7.2), the union class-key shall
be used to refer to a union (Clause 9), and either the class or
struct class-key shall be used to refer to a class (Clause 9)
declared using the class or struct class-key.
In other words, if the primary template is a union, the "redeclaration, partial specialization, explicit specialization or explicit instantiation" must use union; otherwise it can use either class or struct, but not union.
In Professional C++ (2e page 689) it says:
Only for constructors and the destructor you should use X and not
X<T>.
So:
template<typename T>
class X{
X(); // X
~X(); // X
X<T>& operator=(const X<T>& rhs); // X<T>
};
Why should you not use X<T> for constructor and destructor declarations?
The quote is, quite simply, wrong.
X<T> is the actual name of the type:
[C++11: 14.2/6]: A simple-template-id that names a class template specialization is a class-name (Clause 9).
…and you can use it everywhere:
template<typename T>
class X
{
public:
X<T>() {}
~X<T>() {}
X<T>& operator=(const X<T>& rhs) {}
};
int main()
{
X<int> x;
}
(live demo)
You may optionally use X as a "shorthand", whereby the template argument will be automatically appended for you (kinda):
[C++11: 14.6.1/1]: 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 <>.
…but it's certainly not required anywhere.
It sounds like the author is trying to enforce a style guide, but has made it insufficiently clear that it is entirely up to you.
Here there is no relevance to X , it is just a class name.
Since constructors and destructors share the same name as the class and there is no return type for both, there is no need to specify it.
With C++11's strongly typed enums, it is possible to declare a member enumeration of a class like so:
class X {
public:
enum class E;
};
enum class X::E { a, b };
However, when making X a class template:
template <typename T>
class X {
public:
enum class E;
};
template <typename T>
enum class X<T>::E { a, b };
gcc 4.7.2 and clang 3.0 both complain with "error: ‘enum X::E’ is an enumeration template [-pedantic]" and "error: enumeration cannot be a template", respectively. The section of the standard I think is relevant (and which, in fact, this question originated from) is §14 Templates, the first paragraph of which states:
The declaration in a template-declaration shall
declare or define a function or a class, or
define a member function, a member class, a member enumeration, or a static data member of a class template or of a class nested within a class template, or
define a member template of a class or class template, or
be an alias-declaration.
(emphasis mine). So is this a compiler bug, or am I mis-interpreting the statement entirely?
I have been asked for creating this answer. See paragraph [temp.mem.enum] 14.5.1.4/1 of the C++ standard:
An enumeration member of a class template may be defined outside the
class template definition. [ Example:
template<class T> struct A {
enum E : T;
};
A<int> a;
template<class T> enum A<T>::E : T { e1, e2 };
A<int>::E e = A<int>::e1;
—end example ]
Newer version of clang (3.4) compiles your code successfully with flag -pedantic-errors whereas gcc 4.8.1 still considers it is an error. I think it is a gcc bug.
Why does the following works?:
template<typename T> class example {
public:
T val;
example() {val=0;}
example operator+(example ob) {
example temp;
temp.val = val+ob.val;
return temp;
}
};
int main() {
example<int> a;
a+a;
return 0;
}
If I hadn't seen it compiling I would have said that the operator overload should had been as follows:
example<T> operator+(example<T> ob {
example<T> temp;
temp.val = val+ob.val;
return temp;
}
Also, I tried to change the following in main:
example<int> a;
to:
example a;
but got an error saying "...missing template arguments..."
My guess is that inside the class definition, compiler treats example as example. But as this is just a guess and I haven't been able to confirm it anywhere, thought I'd ask here.
Yes, the template arguments can be omitted when you are inside the template definition (but, for example, not in main, because that is outside the template definition).
When omitted, the arguments will be replaced with the arguments of the current instantiation. I.e. when you instantiate the template as example<int>, all occurences of example without template argument inside the template definition will be replaced with example<int> for the purpose of that instantiation.
From the Standard (C++11, emphasis mine):
(14.6.2.1/1) A name refers to the current instantiation if it is
— in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name (Clause 9) of the class template or nested class,
[...]
A few sections later, the Standard gives an example of this:
template <class T> class A {
A* p1; // A is the current instantiation
A<T>* p2; // A<T> is the current instantiation
/*...*/
};
It's because of the injected-class-name (from 9p2):
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.
and from 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 <>.
The injected-class-name refers to the example<T> inside the class itself allowing the shorthand version.
If I understand your question correctly, Yes, within the class definition, you dont need to make each function a template of the class template parameter.
Indeed if you had to define the function outside the class it would need to be as below.
template<typename T>
example<T> example<T>::operator+(example<T> ob) {
p.s. you should change the arguement to const example<T>& ob to reduce unnecessary copies.