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.
Related
I am using a class that encapsulates an std::variant of template specializations, such as:
template<typename Type> struct Generic_node {...};
struct Leaf_node : Generic_node<...> {...};
struct Inner_node : Generic_node<...> {...};
struct Node {std::variant<Leaf_node, Inner_node> variant_;};
I am trying to construct a Node from a function in Generic_node using a converting move constructor, but the compilation fails.
I defined a template constructor, which accepts an rvalue reference (assuming one of the specialized classes) and constructs the variant by moving the value to the variant, where I expect to call the converting move constructor # (4).
When i was trying to create minimal non-working example, i found that the problem really shows only from the template function, where if I knew the exact type (Leaf_node == Generic_node<...> which the compiler knows), the move construction would succeed. Therefore I assume there is as always some template magic happening that I did not anticipate.
#include <variant>
template<typename T>
struct Base
{
void f();
};
struct Derived : Base<int> {};
struct Variant
{
// In real program expecting the T to be one of the multiple variants
// Here I use only one variant because it suffices to illustrate the problem
template<typename T>
Variant(T&& t)
:
variant_ {std::move(t)}
{
}
std::variant<Derived> variant_;
};
template<typename T>
void
Base<T>::
f()
{
Variant {std::move(Derived {})}; // can call on exact type
Variant {std::move(Base<T> {})}; // can not call on specialized template type
}
int
main()
{
Derived {}.f();
}
Relevant compiler error message (clang 7, libstdc++-8):
note: candidate template ignored: substitution failure [with _Tp = Base<int>,
$1 = void, $2 = void]: implicit instantiation of undefined template
'std::variant<Derived>::__to_type_impl<18446744073709551615, false>'
variant(_Tp&& __t)
The problem most probably does not have anything to do with variants, but with the equality of Base<T> == Derived in the template instantiation of the Variant constructor, which the compiler as if did not see.
What is happening in the template instantiations, why can't the compiler call the provided constructor?
Edit: Since I intended to create a specialization, I forgot that inheritance can not imply class type equality, even if it technically is in this special case. This is therefore an easy task of constructing Derived by move from the specialized Base:
struct Derived : Base<int>
{
Derived() = default;
Derived(Base<int>&&) {}
};
If I am correct, the constructor needs to be explicitly defined for each derived class of Base.
In the example you are giving, the Derived class is a separate class from Base. They have exactly the same members, same methods, but they are still separate classes.
Easiest way to solve it would be to use a using statement instead of declaring it as a separate class:
using Derived = Base<int>;
Consider the following example:
template <typename T>
class A {
private:
typedef typename T::C C;
};
template <typename T>
class B : public A<B<T>> {
public:
typedef T C;
};
int main() {
B<int> b;
}
Compiling it with GCC gives the following error:
test.cc:5:23: error: no type named 'C' in 'B<int>'
typedef typename T::C C;
~~~~~~~~~~~~^
test.cc:9:18: note: in instantiation of template class 'A<B<int> >' requested here
class B : public A<B<T>> {
^
test.cc:15:10: note: in instantiation of template class 'B<int>' requested here
B<int> b;
^
Why does compiler give an error if B::C is defined and how to fix it?
At this point,
class B : public A<B<T>> {
… class B is incomplete. Class A can't look inside it.
The C type definition inside B is accessible from that point inside B, and on. It's also available inside function bodies in B because you can regard a function definition inside the class definition as a shorthand for placing it after the class. But an incomplete class contains nothing as viewed from outside: all that outside code can do is form pointers and references and use the class as template argument.
template< class C >
using Ungood = typename C::Number;
struct S
{
void foo() { Number x; (void) x; } // OK
Ungood<S> uhuh; //! Nyet.
using Number = double;
};
auto main() -> int {}
You can fix your code by changing the design. The most obvious is to pass the type as a separate template argument. But depending on what you're trying to achieve it may be that the inheritance you currently have, isn't really needed or even useful.
You can't because you're in a chicken-egg paradox. The definition of the base requires knowledge of the definition of the derived, which needs the definition of the base to complete. You simply have to come up with an alternative. One example would be to use an external metafunction to communicate the needed type to whoever needs it. Hopefully that's not in any part of the definition of the base's members or you're probably screwed.
Other alternative is to pass T as a second parameter.
You can't do that because of this:
A class is considered defined after the closing brace of its class-specifier has been seen [...]
And a few exceptions, none of which are valid in your case.
In other terms, you must consider your derived class as not fully defined when you try to use it in your base class to access the type C.
Anyway, you can exploit the fact that your derived class is a template class and do this:
template <typename T>
class A;
template <template<typename> class D, typename T>
class A<D<T>> {
private:
using C = T;
};
Aa you can see, I've not given a definition for the primary template class, thus only the specialization for template classes can be used.
Not sure this is the OP's real case, but it's the case in the example in the question.
Just curious, is it ever possible to inherit from a template class and in constructor of the derived class, call constructor of the base class which is also templated and has no arguments to deduce its types from?
template<typename T>
struct Base {
template<typename D>
Base() { // no argument of type D to infer from
static_assert(std::is_same<T,D>::value, "");
}
};
struct Derived : Base<int> {
Derived() : Base<int>::Base<int>() {} // is there a way to write it correctly?
};
I can replace template constructor by a template method in my particular case, but still it is an interesting question about the language flexibility.
What the C++ standard says about this (section 14.8.1):
[ Note: Because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. — end note ]
It's a note, not a rule, because it actually is a consequence of two other rules, one in the same section:
Template arguments can be specified when referring to a function template specialization by qualifying the function template name with the list of template-arguments in the same way as template-arguments are specified in uses of a class template specialization.
and from 12.1
Constructors do not have names.
The template arguments of constructor templates must be deduced from their arguments, it's not possible to explicitly specify template arguments for constructors.
As such, have Base take a dummy parameter that deduces the argument:
template <typename T>
struct dummy { }; // to prevent instantiation of T
template <typename T>
struct Base
{
template <typename D>
Base(dummy<D>)
{
static_assert(std::is_same<T, D>::value, "");
}
};
struct Derived : Base<int>
{
Derived() : Base<int>(dummy<int>{}) { }
};
By the way the question is formulated it looks going towards a nonsensical paranoia.
Just think for plain classes:
class Base
{
public:
Base() {}
};
class Derived: public Base
{
public:
Derived() //< default ctor
:Base //< this is the Base type
() //< this selects what ctor to call
{}
};
Note that you call :Base(), that resolve into Base::Base(), not :Base::Base()
Now, by templetizing Base::Base() you are in fact trying to admit that there can be many different default ctor (with ()) for Base. That's a nonsense respect ot the concept itself of "default".
Even if Base is not by itself a template, this is not possible:
class Base
{
public:
template<class T>
Base() {} //< are there infinite way to default construct Base ?!?
};
Base b; //< so how is b constructed ?
Thing gets only apparently different with varadics:
template<class T>
class Base
{
public:
template<class... S>
Base(S&&... s) { /* something to do */ }
};
class Derived: public Base<int>
{
public:
template<class... S>
Derived(S&&... s) //< Derived varadicly initialized
:Base //< base type ...
(std::forward<S>(s)...) //< ... to initialize with what
{}
};
Note that in case s is empty you are in fact calling Base::Base() from Derived()::Derived(), templetized with <> (no args)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is there a way to prevent a class from being derived from twice using a static assert and type trait?
What I'd like to prevent is more than one of the C based template from being derived in D (i.e. there should only ever be one instance of C derived from). Was hoping for maybe a static assert in C or B that may solve this.
// My Classes
template <class T>
class A {};
class B {};
template <class T, class S>
class C : public B, public virtual A<T> {};
// Someone elses code using my classes
class D : public C<Type1, Type2>, public C<Type3, Type4>
{
};
As it stands, it's impossible for B or C to detect what else a more derived class inherits from, so you can't add an assertion there. However, by adding a "curiously recursive" template parameter, you can tell C what the derived class is. Unfortunately, this does require the derived class to give the correct template argument, and there's no way to enforce that.
You can then determine whether the derived class inherits from B in more than one way; it is a base class, but you can't convert a derived class pointer to B* (since that conversion is ambiguous). Note that this doesn't necessarily indicate multiple inheritance; the test will also fail if there's non-public inheritance.
So the best solution I can think of is:
#include <type_traits>
template <class T> class A {};
class B {};
template <class T, class S, class D>
class C : public B, public virtual A<T> {
public:
C() {
static_assert(
std::is_base_of<C,D>::value && std::is_convertible<D*,B*>::value,
"Multiple inheritance of C");
}
};
struct Type1 {};
struct Type2 {};
struct Type3 {};
struct Type4 {};
class Good : public C<Type1, Type2, Good> {};
class Evil : public C<Type1, Type2, Evil>, public C<Type3, Type4, Evil> {};
int main()
{
Good good;
Evil evil; // Causes assertion failure
}
I had to put the assertion in the constructor rather than the class definition, since some of the types are incomplete when the class template is instantiated. Unfortunately, this means that the error will only be reported for classes that are actually instantiated.
I'm trying to implement a kind of CRTP (if I well understand what it is) with multiple inheritance.
My main goal is to have a unified way to access list of instances of each subclass.
May problem seems to reside in the namespace utilization.
Here is the code of the simplest version :
http://ideone.com/rFab5
My real problem is more similar to :
http://ideone.com/U7cAf
I have an additional warning using clang++ :
test.cpp:28:63: warning: static data member specialization of 'instances' must originally be declared in namespace 'NS1'; accepted as a C++0x extension [-Wc++0x-extensions]
template <> std::list<NS1::Derived*> NS1::Base<NS1::Derived>::instances;
^
test.cpp:15:34: note: explicitly specialized declaration is here
static std::list<T*> instances;
Problem has been updated since it does not behave the same using namespaces.
Problem re-edited to post code on Ideone
The problem is that you've tried to define the list variable wrong. You need to provide a definition for Base, in general- you don't just define it for the one part that happens to be Derived's subclass, unless it's an explicit specialization.
template<typename T> std::list<T*> NS1::Base<T>::instances;
http://ideone.com/Vclac
Compiles with no errors. There are no intermediates or anything like that required.
Changing Base() and Intermediary() to Base<U>() and Intermediary<Derived> in the constructors makes the code OK for GCC.
There is no reason to change the definition of instances in the second case: the template is identical as the first situation.
Afaik, you got the following options.
First, if Intermediate is always templated on the derived type, you don't need a list for it, because it will never be the most derived type. If it could be templated on other types / not be derived, you can add a defaulted non-type bool template parameter like so:
template<bool, class A, class B>
struct select_base{
typedef A type;
};
template<class A, class B>
struct select_base<false,A,B>{
typedef B type;
};
template<class T, bool IsDerived = false>
class Intermediate
: public select_base<IsDerived,
Base<T>,
Base<Intermediate<T> >
>::type
{
// ...
};
// derived use
class Derived : public Intermediate<Derived, true>
{
// ...
};
// non-derived use:
Intermediate<int> im;
If the intermediate class is not templated and does not already derive from Base, you need to derive from Base again in the most derived class:
class Derived : public Intermediate, public Base<Derived>
{
// ...
};
The big problem comes when the intermediate also derives from Base but is not templated. You can add a defaulted derived type, but that would make the non-derived use a bit more ugly:
#include <type_traits> // C++0x, use std::
//#include <tr1/type_traits> // C++03, use std::tr1::
struct nil_base{};
template<class Derived = nil_base>
class Intermediate
: public select_base<std::is_same<Derived,nil_base>::value,
Base<Intermediate<Derived> >, //
Base<Derived>
>::type
{
// ...
};
// derived use now without boolean flag
class Derived : public Intermediate<Derived>
{
// ...
};
// non-derived use a bit uglier
Intermediate<> im;
// ^^ -- sadly needed
The following compiles OK with MinGW g++ 4.4.1, MSVC 10.0, and Comeau Online 4.3.10.1:
#include <list>
template <class T>
class Base
{
protected:
Base()
{
instances.push_back(static_cast<T*>(this));
}
private:
static std::list<T*> instances;
};
template <class U>
class Intermediary : public Base<U>
{
protected:
Intermediary()
:Base<U>()
{
}
};
class Derived : public Intermediary<Derived>
{
public:
Derived()
:Intermediary<Derived>()
{
}
};
template<class Derived> std::list<Derived*> Base<Derived>::instances;
int main()
{}
The instances definition is copied verbatim from your question.
I say as Isaac Newton, I frame no hypotheses!
Cheers & hth.,