Forward-declare a member enumeration of a class template - c++

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.

Related

What is a member enumeration in context of templates?

The C++20 standard (N4892) states:
The declaration in a template-declaration (if any) shall
[...]
(2.2) — define [...] a member enumeration, [...]
(13.1.2)
What is meant by a member enumeration in this context? I looked through the standard but could not find a definition of this term, only usages of it. In 13.9.2.3.(1/2) both scoped and unscoped member enumerations are mentioned. So I assume enums are meant by it. However, I was unable to create a member enum template in MSVC:
struct S
{
template<typename T>
enum class e
{
i = 0,
};
};
C3113: an 'enum' cannot be a template
I also have never seen an enum template in the wild, so I assume this isn't possible. So what is meant by a "member enumeration" in context of templates?
However, I was unable to create a member enum template in MSVC:
Because this was not meant. There are no enum templates in the now existing C++ standards. Think instead of a forward-declared scoped-member-enum of a class template:
template <typename T>
class Foo { enum class bar; };
template <typename T>
enum class Foo<T>::bar { baz };
This is a case of a declaration in a template-declaration which is a definition of an enum, which is an example for a use case covered by [temp.pre] 2.2.

template<> for the explicit specialization of a member enumeration

According to 17.7.3 [temp.expl.spec] paragraph 5 (N4659),
... Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax. The same is true when defining a member of an explicitly specialized member class. However, template<> is used in defining a member of an explicitly specialized member class template that is specialized as a class template.
The explicit specialization of E definitely does not belong to the bold case, and it still needs template<>. Why is that?
template<class T> struct A {
enum E : T;
};
template<> enum A<int>::E : int { eint };
This paragraph is related to members of an explicitly specialized class template, but you have not explicitly specialized the class template. This is an example of the case it is talking about:
template<class T> struct A {
enum E : T;
};
template<> struct A<int> {
enum E : int;
};
enum A<int>::E : int { eint }; // no template<> here
In your example code, you have explicitly specialized a member of the primary template, which does require using template<>, as specified in the first paragraph.
1 An explicit specialization of any of the following:
...
(1.7) — member enumeration of a class template
...
can be declared by a declaration introduced by template<>; that is:
explicit-specialization:
template < > declaration
The underlying principle behind paragraph 5 is that once you've explicitly specialized a template, it is no longer a template, and you work with the specialization just like you would any other non-template entity.

Declaring friend class template via wrapper

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.

Template base constructor call in member initialization list error

I have a base class that looks like the following:
template<typename T>
class Base
{
public:
Base(int someValue);
virtual T someFunc() =0;
};
template<typename T>
Base<T>::Base(int someValue)
{}
And then the following:
#include "base.hpp"
class Foo
: public Base<Foo>
{
public:
Foo(int someValue);
virtual Foo someFunc();
};
Foo::Foo(int someValue)
: Base(someValue)
{}
I get the following error from gcc 4.2.1.
error: class ‘Foo’ does not have any field named ‘Base’
I should mention this compiles fine on my Fedora box which is running gcc 4.6.2. This error occurs when compiling on my os x Lion machine.
EDIT
Problem seems to be that I am not indicating type of template in the Foo class when calling the constructor. The following fixes the error in os x.
: Base<Foo>(someValue, parent)
EDIT
Yes this does look like a bug. What I mentioned before fixes the error under os x and code compiles fine in fedora with that fix. Will go and see if there is an update to gcc in os x.
First:
[C++11: 12.6.2/3]: A mem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.
[ Example:
struct A { A(); };
typedef A global_A;
struct B { };
struct C: public A, public B { C(); };
C::C(): global_A() { } // mem-initializer for base A
—end example ]
And Base should be a valid injected-class-name for the base here (that is, you can use it in place of Base<T>):
[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 <>.
[C++11: 14.6.1/3]: The injected-class-name of a class template or class template specialization can be used either as a template-name or a type-name wherever it is in scope. [ Example:
template <class T> struct Base {
Base* p;
};
template <class T> struct Derived: public Base<T> {
typename Derived::Base* p; // meaning Derived::Base<T>
};
template<class T, template<class> class U = T::template Base> struct Third { };
Third<Base<int> > t; // OK: default argument uses injected-class-name as a template
—end example ]
I haven't found anything to indicate that this doesn't apply in the ctor-initializer, so I'd say that this is a compiler bug.
My stripped-down testcase fails in GCC 4.1.2 and GCC 4.3.4 but succeeds in GCC 4.5.1 (C++11 mode). It seems to be resolved by GCC bug 189; in the GCC 4.5 release notes:
G++ now implements DR 176. Previously G++ did not support using the
injected-class-name of a template base class as a type name, and
lookup of the name found the declaration of the template in the
enclosing scope. Now lookup of the name finds the injected-class-name,
which can be used either as a type or as a template, depending on
whether or not the name is followed by a template argument list. As a
result of this change, some code that was previously accepted may be
ill-formed because
The injected-class-name is not accessible because it's from a private base, or
The injected-class-name cannot be used as an argument for a template template parameter.
In either of these cases, the code can be fixed by adding a
nested-name-specifier to explicitly name the template. The first can
be worked around with -fno-access-control; the second is only rejected
with -pedantic.
My stripped-down testcase with Qt abstracted out:
template <typename T>
struct Base { };
struct Derived : Base<Derived> { // I love the smell of CRTP in the morning
Derived();
};
Derived::Derived() : Base() {};

C++ Inheriting from Undefined Template Type

This code:
template <class T>
class Foo {};
typedef Foo<void*> Bar;
template <class T>
class Foo<T*> : public Bar {};
// use Foo<int*> somewhere.
Compiles and works fine in MSVC 9.0, but doesn't compile in GCC 4.1.1 or GCC 4.3.4, with the error:
error: invalid use of undefined type 'class Bar'
Is this illegal C++ that MSVC accepts incorrectly, or a limitation of GCC?
Either way, how can I work around this get the desired behaviour: pointer specialisations of Foo that inherit from unspecialised Foo<void*>?
You cannot do that, except by writing the specialization for all T*, except when T is void. Otherwise, you will derive the class from itself, which for obvious reasons can't work.
Instantiating the primary class template for arguments that have an explicit or partial specialization is not possible. If you try to, by provoking an instantiation before the explicit or partial specialization is visible (note that your code did not provoke such an instantiation), your program is ill-formed, with no diagnostic being required (which effectively renders the behavior undefined).
To achieve the above work-around, you can use SFINAE
template <class T>
struct isnt_void { typedef void type; };
template<> struct isnt_void<void> { };
template <class T, class = void>
class Foo {};
template <class T>
class Foo<T*, typename isnt_void<T>::type> : public Foo<void*> {};
The typedef is a red herring.
The following code is equivalent:
template <class T>
class Foo {};
template <class T>
class Foo<T*> : public Foo<void*> {};
It should be clear that, although Foo<T*> is declared at this point, it is not defined. And thus you may not use it as a base.
[class.name] (2003 wording, 9.1/2):
A class definition introduces the class name into the scope where it is defined
[class.mem] (2003 wording, 9.2/2):
A class is considered a
completely-defined object type (3.9)
(or complete type) at the closing } of
the class-specifier. Within the class
member-specification, the class is
regarded as complete within function
bodies, default arguments and
constructor ctor-initializers
(including such things in nested
classes). Otherwise it is regarded as
incomplete within its own class
member-specification.
[class.derived] (2003 wording, 10/1):
The class-name in a base-specifier shall not be an incompletely defined class (clause 9);
A superior solution would be to compose of Foo<void*>. After all, you don't want the raw void* interface cluttering up your stuff, and you don't want a Foo<T*> to be convertible to a Foo<void*>.
Alternatively, you could fully specialize Foo<void*> beforehand.
Assuming, of course, that you're doing this for type folding, instead of because you actually want inheritance.