Using a member struct of a templated class [duplicate] - c++

This question already has answers here:
C++ - Use enum from template class without template parameter
(2 answers)
Closed 3 years ago.
Is it possible to use the member struct or enum class of a templated class, without explicitly specifying the template arguments. So for ex:
template<typename A, typename B>
class Foo {
public:
enum class Status {E1, E2, E3};
};
void func() {
auto e = Foo::Bar::E1;
}
I guess I see the point that the compiler needs to instantiate Foo given its template arguments, but just wondering if there is a way around to still use its member class.

If the type is not really important and you just want access to the enum class, maybe you can typedef it
using Bar = Foo<void, void>;
void func() {
auto e = Bar::Status::E1;
}
I think your other option is to write the type explicitly or declare the enum class in namespace scope

C++ does not allow use of a Template class without passing template parameters. However there is a workaround if your requirement is not restricted to two template parameters.
The workaround:
You can define the class Foo as variadic template. This will enable you to use the enum without passing parameters to the class Foo. Example is below:
template<typename ...A>
class Foo {
public:
using Bar = enum class Status {E1, E2, E3};
};
void func() {
auto e = Foo<>::Bar::E1;
}
I am not sure if this will satisfy your requirement, but this will compile and work. I did compile it with c++17 flag.

Related

Why isn't it possible to use CRTP's types, but possible to call CRTP's methods? [duplicate]

This question already has answers here:
C++ compiler does not check if a method exists in template class
(2 answers)
Closed 6 months ago.
This code compiles and works only when calling CRTP's methods:
template <typename CRTP>
struct crtp
{
// using type = typename CRTP::type; // will not compile
void print() const
{
using type = typename CRTP::type; // compiles
static_cast<const CRTP&>(*this).print();
}
};
#include <iostream>
int main()
{
struct foo : crtp<foo>
{
using type = int;
void print() const {
std::cout << "John Cena\n";
};
} f{};
const crtp<foo>& c = f;
c.print();
return 0;
}
Upon crtp<foo>'s instantiation, foo is incomplete. But its methods can be used.
However, CRTP's types can't used dues CRTP's incompleteness outside functions.
Why is it allowed to defer a check for incompletness until a class's function is called, but not upon object's instatiation?
When using CRTP::type within the class body (uncommented), one will get a compilation error:
<source>:4:33: error: no type named 'type' in 'foo'
using type = typename CRTP::type; // will not compile
~~~~~~~~~~~~~~~^~~~
Note that even if you write a member function definition inside the class template definition, it is not instantiated when the class template is instantiated.
foo can't be defined until its base class crtp<foo> has been completely defined, but the type alias in the definition of crtp<foo> requires foo's definition to be known, which requires crtp<foo> to already be defined, which requires foo, and so on...
Member functions, on the other hand, are not compiled until after their class has been fully defined, so the type alias is fine there.
(That is, instantiating void crtp<foo>::print() const works because it only happens after foo has been defined and crtp<foo> has been instantiated.)
Separating the class template definition from the member function definition makes the two phases clearer:
template <typename CRTP>
struct crtp
{
// Can't compile unless CRTP is defined at this point.
using type = typename CRTP::type;
void print() const;
};
template <typename CRTP>
void crtp<CRTP>::print() const
{
// Also can't compile unless CRTP is defined at this point, but
// this is a later point than the class definition.
using type = typename CRTP::type;
static_cast<const CRTP&>(*this).print();
}
Within CRTP, derived classes are incomplete:
struct foo : crtp<foo> // foo incomplete here
{
using type = int;
void print() const {
// foo complete here
std::cout << "John Cena\n";
}
}; // foo complete here
foo is also complete in void crtp<foo>::print() const.

Obtain using definition from templated class

Given the following classes:
// Some random class
class A { };
// A templated class with a using value in it.
template<class TYPE_B>
class B {
public:
using TYPE = TYPE_B;
};
Next we use these two classes in class C. But if we are using B as the template parameter we would like to obtain the TYPE defined in it.
template<class TYPE_C>
class C {
// A check to see if we have a class of type B
static constexpr bool IS_B = std::is_same<B<int32_t>, TYPE_C>::value ||
std::is_same<B<int64_t>, TYPE_C>::value;
public:
// This is what not works. How to get B::TYPE here?
using TYPE = std::conditional<IS_B, TYPE_C::TYPE, TYPE_C>;
};
Class C would we used like:
C<A> ca;
C<B<int32_t>> cb32;
C<B<int64_t>> cb64;
I am compiling this in GCC. My fear what I would like not have to do is to use the std::is_same statement for each type used with B. Put that in the std::conditional. Are there any alternatives?
You have two issues in your code:
You are missing a typename before TYPE_C::TYPE. Since TYPE_C::TYPE is a dependent name, you need to use typename to tell the compiler that you are looking for a type.
You cannot use TYPE_C::TYPE ins the std::conditional1 expression because when TYPE_C is not B<>, that expression is invalid, but will still be evaluated.
One simple solution is to use an intermediate template to access the type, using template specialization, e.g.
template <class T>
struct get_C_type {
using type = T;
};
template <class T>
struct get_C_type<B<T>> {
using type = T;
};
template<class TYPE_C>
class C {
public:
// you still need a typename here
using TYPE = typename get_C_type<TYPE_C>::type;
};
1 You probably want to use std::conditional_t or std::conditional<>::type here.

What is the use of typedef inside the class

I have some code like this
class A : public b<T>
{
public:
typedef b<T> _baseclass; // why we need this declaration
};
What is the use of typedef inside the class?
Is the definition limited to this class only?
Shall we create this as static and use without crating an object of the class?
This member type will be available outside of the class definition too, which is convenient in template code. If you passed an A into a function template, or maybe some other classes that also have _baseclass member types, then you can use _baseclass to find out what the base is without needing to know exactly what the top-level type is.
Standard templates like std::vector and std::map have member types like value_type — these do not signify a base class but have a similar purpose, in that you can use value_type anywhere a container is used, no matter which container is used.
Swapping typedef to using (because I want to), here's an example:
// The class templates
template <typename T>
struct Base {};
struct A : Base<int>
{
using base_class = Base<int>;
};
struct B : Base<char>
{
using base_class = Base<char>;
};
struct C : Base<bool>
{
using base_class = Base<bool>;
};
// The example
template <typename T>
void foo()
{
// typename needed because base_class is a "dependent name"
// (just go with it)
typename T::base_class the_base;
// This line is to suppress "unused variable" warnings
(void)the_base;
}
int main()
{
foo<A>();
foo<B>();
foo<C>();
}
Though this particular program doesn't actually "do anything", it shows a function template foo that can "know" what the base class was in each case, without any further information about exactly what T is. And it'll work for any class to which you've added a base_class member type!

How to refer to a type defined in a derived class passed as a template argument?

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.

Accessing public static members of a templated class without instantiating the template?

I have a templated class and want to access a public static variable from outside it, but I can't figure out any way to do so without instantiating the template. This code:
template<class T>
class TemplatedClass {
public:
static const int static_member = 10;
};
...
int i = TemplatedClass::static_member;
Produces the following error: "'template class TemplatedClass' used without template parameters."
If I instantiate the class when accessing the variable:
int i = TemplatedClass<int>::static_member;
The error goes away. I would prefer not to have to instantiate a template in a context where it doesn't really make sense with a dummy type argument just to suppress an error. If I have to, what would be the best dummy type to use? I tried <> and <void>, but neither worked.
Can't be done, since specializations might override the value, i.e:
template<class T>
class TemplatedClass : public BaseClass
{
static const int value = 42;
};
template<>
class TemplatedClass<StarTrek>
{
static const int value = 47;
}
Thus you will get different values:
TemplatedClass<StarTrek>::value != TemplatedClass<void>::value
If the values are to be equal, I strongly suggest you add a non-template base class:
class BaseClass {
public:
static const int value = 42;
};
template<class T>
class TemplatedClass : public BaseClass
{
...
}
Instantiating or explicitly a dummy type (i.e. void) might work, but you might get compile errors depending on how you use your template parameter.
int x = TemplatedClass<void>::value;
So, please write code which show your intentions clearly, i.e. common values for all instantiations should not be in the type-dependent template class. If you can't have that, please explain what you're trying to do in more detail.
Using a dummy type might work for trivial classes, but not if things get more complex.
Let's imagine, that your class "continues" like this:
template<class T>
class TemplatedClass {
public:
static const int static_member = 10;
typedef typename std::enable_if< std::is_integral< T >::value >::type type;
};
This code tells us that T cannot be non-integral type.
Upd (thanks to jogojapan):
That's why in some cases you cannot use any type as a dummy one