template class inheritance problem - c++

Can you please tell me, what am I missing?
template <class T> struct Base
{
T data;
Base(const T &_data):data(_data) { }
};
template <class T> struct Derived : Base<T>
{
Derived():Base(T()) {} //error: class 'Derived<T>' does not have any field named 'Base'
};

template <class T> struct Derived : Base<T>
{
Derived():Base<T>(T()) {}
};

There's still the question: Who's right? GCC is right here. Unqualified name lookup does not look into dependent base classes, hence will not find Base in the scope of Base<T>. You can change your code to the following Standard conformant variant too
Derived():Derived::Base(T()) {}
If I remember correctly, this is only supported by GCC4.5 though. Earlier versions didn't implement injected class name lookup properly.

Related

Constrained CRTP Premature Rejection

I'm trying to implement a derived class inheriting from a base template, with the derived class as its template parameter (the example below hopefully clears things up):
template <class T>
struct S
{
T f() {return T();}
};
struct D : public S<D>
{
};
This compiles and works well on gcc, clang, and msvc as well. Now, I want to "make sure" that the template parameter inherits from the base class:
#include <concepts>
template <class T>
concept C
= requires ( T t )
{
{ t.f() };
};
template <C T>
struct S
{
T f() {return T();}
};
struct D : public S<D>
{
};
However, this gets rejected by every compiler, with clang providing the most insight:
error: constraints not satisfied for class template 'S' [with T = D]
struct D : public S<D>
^~~~
note: because 'D' does not satisfy 'C'
template <C T>
^
note: because 't.f()' would be invalid: member access into incomplete type 'D'
{ t.f() };
I understand where the compiler is coming from: D is not fully defined yet when the constraint has to be checked, so it fails in lieu of the necessary information. That said, I'm kinda disappointed that no attempt is made to complete the definition of the derived class before evaluating a yet uncheckable constraint.
Is this behaviour intended? Is there another way to check the inheritance that actually works?
By the way, gcc gives a rather useless error message in this case.
You can check the requirement in the default constructor of the base class
#include <type_traits>
template<class Derived>
class Base
{
public:
Base()
{
static_assert(std::is_base_of_v<Base<Derived>, Derived>);
}
};
class Derived : public Base<Derived>
{ };
This must also be checked in any other user defined non-copy and non-move constructors of base. This is valid as Derived is fully defined when the constructor is instantiated.

Why must we specify template arguments for template base class when calling its constructor? [duplicate]

I was porting some code from MSVC(without permissive-) to linux and I learned that if you call constructor of a template base class in initializer list of your class you must specify all the template parameters or you get an error.
Seems kind of redundant, since if you make a mistake in retyping the template parameters it is a hard error:
error: type 'Base<int, true>' is not a direct or virtual base of
'Derived'
full code here:
template <typename T, bool has_x>
struct Base
{
Base(T t): t_(t){
}
T t_=0;
};
template <typename T>
class Derived : public Base<T, false>
{
public:
// : Base<T, true> is hard error
Derived(const T& t) : Base<T, false>(t) {}
};
int main()
{
Derived d(47);
}
Is there a strong reason for this, or just standardization process never took time to special case this use case?
You only need to do that when Derived is a template and the type of the base depends on its template parameters.
This compiles, for example:
template <typename T>
class Derived : public Base<int, false>
{
public:
Derived(const T& t) : Base(t) {}
};
As far as I know, here (in member initializer list) Base is actually the injected-class-name of Base<...>, inherited from it like everything else.
And if the type of the base does depend on the template parameters, its inherited injected-class-name becomes inaccessible (at least directly), just like any other member inherited from it.
For a member variable/function, you'd add this-> to access it, but for a type member you need Derived:::
template <typename T>
class Derived : public Base<T, false>
{
public:
Derived(const T& t) : Derived::Base(t) {}
};

accessing definitions from sub class in templated class

I have trouble to build a class which itself contains a subclass which self derives from class which needs template parameters from the top class. Sound horrible and it is indeed deep inside some MTP construction. But have a look on a simple example which I could shrink from the real code.
I leave the names as is in my original source. They are not important here.
template <typename ... T>
class ConstructAll: public T...
{
public:
using ConstructorParms = int;
using BASES_T = ConstructAll<T...>;
ConstructAll(int){}
};
template <typename T>
class WithTemplate
{
};
class WithoutTemplate
{
};
template <typename X>
class CircuitFromNetlist
{
private:
class SerialReader: public ConstructAll<
WithTemplate<X> // use this-> don't compile
//WithoutTemplate // but this works
>
{
public:
SerialReader( typename BASES_T::ConstructorParms p): BASES_T( p ) {}
};
public:
CircuitFromNetlist()
{
SerialReader ser{1};
}
};
int main()
{
CircuitFromNetlist<int> c;
}
If I use WithTemplate<X>it did not compile and runs into:
main.cpp:31:40: error: 'BASES_T' has not been declared
SerialReader( typename BASES_T::ConstructorParms p): BASES_T( p ) {}
^~~~~~~
main.cpp: In constructor 'CircuitFromNetlist::SerialReader::SerialReader(int)':
main.cpp:31:70: error: class 'CircuitFromNetlist::SerialReader' does not have any field named 'BASES_T'
SerialReader( typename BASES_T::ConstructorParms p): BASES_T( p ) {}
If I flip the code to use non templated class it seems to work.
Some idea to get the thing working?
What you're seeing here is actually correct behavior according to the C++11 standard:
In the definition of a class or class template, if a base class depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. -- [temp.dep] (emphasis mine)
When you inherit from ConstructAll<WithoutTemplate>, the base class does not depend on the template parameter X, but obviously inheriting from ConstructAll<WithTemplate<X>> the base class does depend on this template parameter.
You will have to explicitly refer to the type as typename ConstructAll<WithTemplate<X>>::BASES_T.

Inheriting templated constructor from class template?

What would be the syntax to inherit constructors (some of them are template) from a class template in C++11 ?
template <class T>
struct Base
{
using type = T;
explicit constexpr Base(const type& x): value{x} {};
template <class U> explicit constexpr Base(U&& x): value{std::forward<U>(x)} {};
type value;
}
struct Derived: Base<bool>
{
using Base::Base<bool>; // Does not seem to work ?!?
}
You derive from Base<bool>. So your base class is Base<bool>, and inheriting the constructors is done via
using Base<bool>::Base;
Live on Coliru
You do not need Base<bool> after the ::, in fact the code doesn't compile if you put it. The constructors are still referred to as Base, and not Base<bool>. This is consistent with referring to member functions of class templates: you use e.g. void Foo<int>::f() and not void Foo<int>::f<int>() to refer to the Foo's member function f().

Making a template parameter a friend?

Example:
template<class T>
class Base {
public:
Base();
friend class T;
};
Now this doesn't work... Is there a way of doing this?
I'm actually trying to make a general class sealer like this:
class ClassSealer {
private:
friend class Sealed;
ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{
// ...
};
class FailsToDerive : public Sealed
{
// Cannot be instantiated
};
I found this example on this site somewhere but I can't find it... (here)
I know there are other ways of doing this but just now I'm curious if you actually can do something like this.
It is explicitly disallowed in the standard, even if some versions of VisualStudio do allow it.
C++ Standard 7.1.5.3 Elaborated type specifiers, paragraph 2
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 a
template type-parameter,
the elaborated-type-specifier is
ill-formed. [Note: this implies that,
within a class template with a
template type-parameter T, the
declaration friend class T; is
ill-formed. ]
I recognize the code above as a pattern to seal (disallow the extension of) a class. There is another solution, that does not really block the extension but that will flag unadvertidly extending from the class. As seen in ADOBE Source Library:
namespace adobe { namespace implementation {
template <class T>
class final
{
protected:
final() {}
};
}}
#define ADOBE_FINAL( X ) private virtual adobe::implementation::final<T>
with the usage:
class Sealed : ADOBE_FINAL( Sealed )
{//...
};
While it allows extension if you really force it:
class SealBreaker : public Sealed, ADOBE_FINAL( Sealed )
{
public:
SealBreaker() : adobe::implementation::final<Sealed>(), Sealed() {}
};
It will restrict users from mistakenly do it.
EDIT:
The upcoming C++11 standard does allow you to befriend a type argument with a slightly different syntax:
template <typename T>
class A {
// friend class T; // still incorrect: elaborate type specifier
friend T; // correct: simple specifier, note lack of "class"
};
I found a simple trick to declare template parameters as friends:
template < typename T>
struct type_wrapper
{
typedef T type;
};
template < typename T> class foo
{
friend class type_wrapper < T>::type
}; // type_wrapper< T>::type == T
However I do not know how this could help to define an alternative version of a class sealer.
Do you really need to do this?
If you want to prevent someone from deriving from your class, just add a comment and make the destructor non-virtual.