How to refer to an alias template of a class A given as a template parameter to a class C that inherits from a template base class B?
#include <vector>
struct A
{
// the alias template I want to refer to:
template<class T>
using Container = std::vector<T>;
};
// the base class
template<template<class> class _Container>
struct B
{
_Container<int> m_container;
};
template<class _A>
struct C : public B< typename _A::Container >
{// ^^^^^^^^^^^^^^^^^^^^^^
};
int main()
{
C<A> foo;
}
I tried several solution by adding the template keyword at every possible place in the statement (like template<class T> typename _A::Container<T>, typename _A::template Container...) but g++ gives either "template argument 1 is invalid" or "type/value mismatch"!
The correct syntax would be:
template <class A>
struct C : public B< A::template Container >
{
};
LIVE
BTW: Don't use _A as the name of template parameter, identifiers beginning with an underscore followed immediately by an uppercase letter is reserved in C++.
Related
My understanding is that in C++ user defined types are inherited and indeed this seems to be the case up to a point.
Here is what I'm trying to do but this doesn't compile:
template <typename T>
struct A{
using type = T;
};
template <typename T>
struct B: A<T>{
using type2 = type;
};
However, if I have B extend A of a non template parameter it works:
template <typename T>
struct A{
using type = T;
};
template <typename T>
struct B: A<int>{
using type2 = type;
};
Indeed, one work around I found (but which doesn't really help me for what I'm trying to do is to scope type but this defeats the point of inheritance:
template <typename T>
struct A{
using type = T;
};
template <typename T>
struct B{
using type2 = A<T>::type;
};
Is there a proper way to achieve the behavior I want (in a more general case, this is obviously a toy example) and what are the semantics of this?
Edit: this is what I wish the code would look like, the lack of inheritance can be worked around but I wanted to understand what the limitations are
template <typename t>
struct instruction{
public:
using input = t::first;
using mem = t::second;
using newmem = mem;
using newinput = input;
using result = pair<newinput, newmem>;
};
template <typename t>
struct right_code: public instruction<t>{
using newmem = buffer<cons<mem::list2::head, mem::list1>, (std::is_same<mem::list2::tail , nil> ? cons<zero, nil>
: mem::list2::tail)>;
};
template <typename t>
using right = right_code<t>::result;
template <typename t>
struct left_code:instruction<t>{
using newmem = (std::is_same<mem::list1::tail, nil ? mem
: buffer<mem::list1::tail, cons<mem::list1::head, mem::list2>);
};
template <typename t>
using left = left_code<t>::result;
Use typename A<T>::type.
template <typename T>
struct A {
using type = T;
};
template <typename T>
struct B : A<T> {
using type2 = typename A<T>::type;
};
using type2 = type would not work for struct B : A<T> because type is a non-dependent name thus name lookup would not be affected by declarations visible on instantiation, see
https://en.cppreference.com/w/cpp/language/unqualified_lookup
For example,
template <typename T>
struct A {
using type = T;
};
template <>
struct A<int> : public C<int> {
// it doesn't need to have `type`
};
A<T> doesn't even need to have type for some specialization. We see that declarations from A<T> are unsure until we instantiate it.
By using typename A<T>::type, we make a dependent name on T, which kind of "postponed" the name lookup to the point of instantiation.
On the contrast, for struct B : A<int>, using type2 = type works totally fine since A<int> is explicitly instantiated so that A<int>::type is visible from the template definition context.
I have this toy example,
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename TChild::template B<T>;
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
using Bar = Child::Foo<int>;
which fails to compile. The intention is that I have a parent class that provides type computations based on members of the child class. The child class is provided via CRTP. However the line
using Foo = typename TChild::template B<T>;
fails to compile:
<source>: In instantiation of 'struct Base<Child>':
<source>:16:16: required from here
<source>:13:11: error: invalid use of incomplete type 'struct Child'
13 | using Foo = typename TChild::template B<T>;
| ^~~
<source>:16:8: note: forward declaration of 'struct Child'
16 | struct Child : Base<Child> {
| ^~~~~
Am I being naive in expecting such a construct to work?
Failing code at https://godbolt.org/z/5Prb84
Let me post another way to do it:
template<typename TChild, class T>
struct GetB {
using Type = typename TChild::template B<T>;
};
template<typename TChild>
struct Base {
template<typename T>
using Foo = typename GetB<TChild, T>::Type;
};
struct Child : Base<Child> {
template<typename T>
using B = T;
};
I don't have a language-lawyer-type explanation why this works, but it should be related to having an additional level of indirection. When a compiler sees
using Foo = typename TChild::template B<T>;
it can (and will) check and complain right at this point that an incomplete type is used. However, when we wrap access to B<T> into a function or a struct,
using Foo = typename GetB<TChild, T>::Type;
then we're not accessing internals of TChild at this point, we're just using the name of it, which is fine.
Issue with CRTP is that derived class is incomplete in CRTP definition, so you cannot use its using.
in
template <typename T>
using Foo = typename TChild::template B<T>;
complete type of TChild would be required because of the ::.
TChild is non-dependent of template T, so first pass check should be done (but fails)
You might use external traits to handle that situation
template <typename C, typename T>
struct Traits_For_Base
{
using type = typename C::template B<T>;
};
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename Traits_For_Base<TChild, T>::type;
};
Traits_For_Base<TChild, T> is dependent of T, so nothing to do for first pass check.
and, with second pass check (dependent of T), Child would be complete.
Demo
or you might change your alias to make in type dependent of the template parameter of the class Base:
template <typename TChild>
struct Base {
template <typename T,
typename C = TChild,
std::enable_if_t<std::is_same_v<C, TChild>, int> = 0> // To avoid hijack
using Foo = typename C::template B<T>;
};
C is dependent of template, so cannot be checked in first phase.
Demo
The following gives the exact same result as required. It's still a mystery as to why it should work when the previous technique does not.
#include <type_traits>
#include <string>
template <typename TChild>
struct Base {
template <typename T>
static auto foo(){
return typename TChild::template B<T>();
}
template <typename T>
using Foo = std::decay_t<decltype(foo<T>())>;
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
static_assert(std::is_same<Child::B<int>,int>::value,"");
static_assert(std::is_same<Child::B<std::string>,std::string>::value,"");
https://godbolt.org/z/b6Y5Tb
The problem is at stands is that instantiation of nested template requires a complete type of enclosing class and a declaration of template B:
template <typename TChild>
struct Base {
// TChild should be complete at the moment of this declaration
// template B should be declared at this moment.
template <typename T>
using Foo = typename TChild::template B<T>;
};
Instantiation of Base
struct Child : Base<Child>
/* TChild = Child at this moment is incomplete */
{
template <typename T>
using B = T;
/* Point where template B begins to exist */
}; /* point where Child is complete */
Those rules are by design of language, and their goal is to avoid forcing compiler to make multiple passes forth and back through code, possibly in infinite recursion, to actually instantiate what did you mean. Weak-typed interpreter languages often don't have such problem as they can "correct" themselves later.
Case 1.
The static function solution works for the reason that there is no type declaration was done. You had declared a template of function which actually has a global scope, but a concrete function or type aren't created yet.
struct Base {
template <typename T>
static auto constexpr getFoo() {
return typename TChild::template B<T>{};
}
};
struct Child : Base<Child> {
template <typename T>
using B = T;
/* At this point we can instantiate Child::getFoo<int>()*/
}; /* Child is complete now */
At this point instantiation of Child::getFoo<T> is possible but all it requires is the return type of function.
using Bar = decltype(Child::getFoo<int>());
You can place this declaration into Child AFTER the declaration B, because B<int> would be complete at this point. You still can't declare it in Base
Case 2.
Your solution declares another template Foo, it doesn't instantiate it within Base. This template isn't explicitly dependant on TChild but required prototype of foo() to exist at point of instantiation.
template <typename TChild>
struct Base {
template <typename T>
static auto foo(){
return typename TChild::template B<T>();
}
// Foo is a template
template <typename T>
using Foo = std::decay_t<decltype(foo<T>())>;
};
The instantiation happens at point where you would use Base::Foo<T>, which you actually didn't. That declaration is a no-op in your solution. It is legal to use it after the B declaration. You can't use it within Base or anywhere before B was declared.
Now what if you actually need to use an instance of B in Base? Here comes trait class solution:
Case 3.
Traits can be templates specialized for child classes or concrete classes, that's a design choice. A trait serves as a base class for the CRTP base class and is a form of mixin. It's role is to provide useful declarations for CRTP. One of possible solutions for the most flexible trait naming:
template <typename TChild, template<class> typename Trait>
struct Base : public Trait<TChild> {
// Trait<TChild>:: tells compiler that Foo is dependant on TChild
// and is declared in base class Trait. As compiler had reached this
// point, the substitution was successful and thus Trait is complete
using Foo = typename Trait<TChild>::template B<int>;
// Foo is assumed to be a complete type, we can use it here!
Foo make_foo() { return Foo{}; }
};
// Declaring trait template in this case.
template <typename T> struct ChildTrait;
// And specializing
template <>
struct ChildTrait<struct Child> {
template <typename T>
using B = T;
};
struct Child : Base<Child,ChildTrait> {
using Bar = typename Base::Foo;
};
static_assert(std::is_same<Child::B<int>,int>::value,"");
static_assert(std::is_same<Child::B<std::string>,std::string>::value,"");
The idea here is that Trait<TChild> = ChildTrait<Child> has to be and can be a complete class within Base, or we could not derive Base from it. With slight modifications (eliding using, static_assert, typename use) this would compile in C++98 as it doesn't require decltype. This method used by some implementation of standard components, e.g. by std:: streams.
Traits may describe concrete storage types, allocators, etc. WHat's important is that resulting concrete types have no relation but have a shared interface declared in Base.
Does this in any way do what you mean?
#include <type_traits>
template <typename TChild>
struct Base {
template <typename T>
static auto constexpr getFoo() {
return typename TChild::template B<T>{};
}
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
using Bar = decltype(Child::getFoo<int>());
static_assert(std::is_same_v<Bar, int>);
I've essentially replaced the template alias with a template static function which returns a default constructed object of the type you wanted the template alias to encode, so decltype-ing its result should give the type you want.
Consider the code below:
template <typename T>
class A{
...
}
template <class U>
class B{
...
}
int main {
B<A<int>> a;
...
}
How can I get the template parameter of A (int in this case) inside B, if A<int> is the template parameter for B?
I could parametrize B as follows, but I feel like I am sending an unnecessary piece of information.
template <class AA, typename T>
class B { ... }
The reason I do not simply use template <typename T> for class B is that I have a pointer to class A inside B, and I want to use the template parameter class AA to see if that pointer is const or not, hence have the correct type for the member pointer in B.
There are several ways, depending of that you might change:
Quick way, specialize B
template <class> class B;
template <class T>
class B<A<T>>
{
// Use directly T
//...
};
Add info in A directly (as std containers do with value_type)
template <typename T>
struct A
{
using my_type = T;
};
// Then in `B<U>`, use `typename U::my_type`
Use external traits to extract information from A (as std::iterator_traits) (that also allows to handle built-in types):
template <typename T>
struct ATrait;
template <typename T>
struct ATrait<A<T>>
{
using my_type = T;
};
// Possibly a generic one
template <template <typename> class C, typename T>
struct ATrait<C<T>>
{
using my_type = T;
};
// Then in `B<U>`, use `typename ATrait<U>::my_type`
I think the following does what you want:
#include<type_traits>
template<class>
class A{};
template<class>
struct get_inner;
template<template<class> class TT, class T>
struct get_inner<TT<T>> {
using outer = TT<T>;
using inner = T;
};
template<class TT>
struct B {
using A = TT;
using inner = typename get_inner<std::decay_t<A>>::inner;
};
int main(int argc, char *argv[])
{
static_assert(std::is_const_v<typename B<const A<int>>::A>);
static_assert(!std::is_const_v<typename B<A<int>>::A>);
}
Note the std::decay_t, it wouldn't work with the const parameter directly (hence we cannot just specialize B in this way). Maybe decay_t is a bit strong but it works^^
Try this
template <typename X> class B;
template <template <typename> class XX, typename T>
class B<XX<T>>
{
// your implementation
};
B<A<int>> a;
I currently use a template class with multiple parameters,
template<class A, class B> class M{ };
However, in the position of class A I want to insert a template class, something like
template<class C> class A{ };
The only solution I've found for doing this is to use template template parameters:
template< template<class C> class A, class B> class M{ };
In my implementation, the only parameterization of A I use is A<B>. I don't need several instantiations of A using different parameters, for example I don't need to instantiate A<int> A<double> and A<long double> in M.
Is there an alternative to the template template parameter? The reason I ask is a follow up of this thread, in which in his answer #Evan Teran says he's only once ever had to use template template parameters...
I guess a twist on my question is: are there downsides to using template template parameters?
Assuming B can somehow be determined from A<B>, you can just take one template parameter:
template <class A> class M
{
typedef typename A::The_B_Type B;
};
Of course, The_B_Type has to be a valid typedef within A<B>. That's one of the reasons why standard library containers provide all the typedefs. For example, if the template A was std::vector, you could do this:
template <class A> class M
{
typedef typename A::value_type B;
};
You can take the instantiated A<B> as an argument, then use a traits class to extract the B passed to A<B> if you need it:
template<typename T>
struct extract_b {}; // SFINAE enabled
template<template<class>class A, typename B>
struct extract_b< A<B> > {
typedef B type;
};
// C++11, you can replace it with typename extract_b<T>::type at point of use
// if you lack support for it:
template <typename T>
using ExtractB = typename extract_b<T>::type;
template<typename A>
struct M {
typedef ExtractB<A> B;
// ...
};
the traits class is poorly named, but you can see that I can get the template argument out of A<B> and expose it in M<A>.
When tryin to compile this (CRTP-like) code with GCC 4.6.0:
template<template<class> class T> struct A;
template<class T>
struct B: A<B<T>::template X> {
template <class U> struct X { U mem; };
};
B<int> a;
I get the errormessage "test.cpp:3:26: error: no class template named ‘X’ in ‘struct B<int>’". Why does X seem to be invisible outside the class definition?
As Emile Cormier correctly points out here the problem is that at the place of instantiation of A, B is still an incomplete type, and you cannot use the inner template.
The solution for that is moving the template X outside of the template B. If it is independent of the particular instantiation T of the template B, just move it to the namespace level, if it is dependent on the instantiation, you can use type traits:
template <typename T>
struct inner_template
{
template <typename U> class tmpl { U mem; }; // can specialize for particular T's
};
template <typename T>
struct B : A< inner_template<T>::template tmpl >
{
};
struct B is still considered an incomplete type when you specify A<B<T>::template X> as the base class.
You're trying to use a member of B as a parent of B creating a recursive-esque situation. For example this doesn't compile either:
template<template<class> class T> struct A {};
struct B : public A<B::nested>
{
struct nested {};
};