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.
Related
I have a class template that has members of some type. This type is determined based on the type that is provided when instantiating the template. It uses a default (double in the example below) unless an override is provided by that class. Classes used as template types may provide this override type (here "Two" provides the override type "int"). If a class provides the override, the override should only be used if the class also sets the UseOverride flag. If flag is absent or false, default "double" should be used.
Problem is that if the template type does not provide the "type", then compiler gives error in below code. I suspect I need to use SFINAE here, but haven't been able to figure out a suitable approach for it, even after puzzling and browsing related questions for a good part of the afternoon.
How to define the EventType template so that it works as intended? I want to keep the EventType<T> syntax.
#include <iostream>
struct One {
//This type is ignored, and double is used, because UseOverride = true is not specified:
using type = short;
};
struct Two {
static constexpr bool UseOverride = true;
using type = int;
};
struct Three {
static constexpr bool UseOverride = false;
//I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since
//the class instructs not to use the override). But compile does generate error.
//How to avoid this?
};
template <typename T, typename = void>
struct overrideInfoProvided : std::false_type {};
template <typename T>
struct overrideInfoProvided<T, decltype((void)T::UseOverride, void())> : std::true_type {};
template <typename T>
constexpr bool Override()
{
if constexpr (overrideInfoProvided<T>::value)
{
return T::UseOverride;
}
return false;
}
template<class T>
using EventType = typename std::conditional_t<Override<T>(), typename T::type, double>;
template <class T>
struct Test
{
typename EventType<T> member;
Test()
{
std::cout << member << std::endl;
}
};
int main()
{
Test<One>();
Test<Two>();
//Gives error:
//Test<Three>();// `type': is not a member of any direct or indirect base class of `three';
}
I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since the class instructs not to use the override). But compiler does generate error. How to avoid this?
Just defer the access to ::type with the below type_identity helper:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using EventType = typename std::conditional_t<Override<T>()
, T
, type_identity<double>>::type;
// ~~~~~~~~~~~~^
DEMO
You are on the right track, but you don't need to have separate checks for the existence of useOverride, and type. Instead, you can do both of the checks in the same sfinae class:
template <typename T, typename = void, typename = void>
struct EventType_T {
using t = double; // default if useOverride or type doesn't exist
};
template <typename T>
struct EventType_T<T, std::void_t<decltype(T::UseOverride)>,
std::void_t<typename T::type>> {
// choose type if useOverride is true, and double otherwise
using t = std::conditional_t<T::UseOverride, typename T::type, double>;
};
template <typename T>
using EventType = typename EventType_T<T>::t;
Here's a demo. This allows you to still use the EventType<T> syntax as before.
Note, the t member instead of type is unconventional, but since we are already testing for a type member in T, this might make it clearer what's going on. I would recommend using type once yo understand how the solution works.
I am trying to use a member type of a template class, which does not depend on any template parameters of the template class. I would like to keep the type as the member type due to its logic, but I do not want to specify the unnecessary template parameters of the class whenever I want to use the member type outside the class.
Consider the following:
class Dummy { };
// Template class
template<typename T>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::template MemberType<Dummy> m1; // okay
typename A::template MemberType<Dummy> m2; // not okay!
return 0;
}
I got the following compiler error when I try to compile using g++:
error: ‘template<class T> class A’ used without template parameters
typename A::template MemberType<Dummy> m2; // not okay!
Is there any workaround for this?
I am trying to use a member type of a template class, which does not
depend on any template parameters of the template class.
As a nested type within class A<T>, MemberType does depend on the template parameter T.
i.e. A<T>::MemberType<T2> and A<U>::MemberType<T2> are distinct classes.
What you want to do is not possible. A template is just a template. There is very little you can do with it before actually instantiating it for a concrete type. There could be a specialization for A that has no nested MemberType at all.
I would like to keep the type as the member type due to its logic,
[...]
...but it seems the logic is something else: The MemberType does not depend on A, hence it should not be part of a template that depends on A.
Sloppy speaking template<typename T> can be read as "everything that follows depends on T". Even if you think it does not, there could always be a specialization that changes anything inside A. If you want MemberType to not depend on T then declare it outside A.
Everything in a template is dependent on the parameter(s) - meaning a template-specialization might not even have class MemberType.
But you can make a default parameter - you still need to write <> though (but you can omit template usually - even typename, but I left that):
class Dummy { };
// Template class
template <class T = void>
class A {
public:
template<typename T2>
class MemberType : public T2 {
public:
T2 t2;
};
};
int main()
{
typename A<Dummy>::MemberType<Dummy> m1; // okay
typename A<>::MemberType<Dummy> m2; // also ok
return 0;
}
As others have pointed out, this somewhat looks like an anti-pattern though - since the inner type is not dependent on the parameter of the outer template class.
Is there any workaround for this?
MemberType is a type dependent from a template parameter so, necessarily, you have to pass through the containing template a template parameter to define it
typename A<SomeType>::template MemberType<AnotherType> m2;
Taking in count that your not interested in external SomeType parameter, the best workaround I can imagine is the use of a using as follows (or something similar)
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
to reduce typewriting.
The following is a full compiling simplified example
#include <type_traits>
class Dummy { };
template <typename>
struct A
{
template <typename T2>
struct MemberType : public T2
{ T2 t2; };
};
template <typename T>
using MemberType_t = typename A<T>::template MemberType<T>;
int main ()
{
typename A<Dummy>::template MemberType<Dummy> m1;
MemberType_t<Dummy> m2; // compile
static_assert( std::is_same<decltype(m1), decltype(m2)>::value, "!" );
}
I want to create a class that accepts only certain types of template classes. I know that there exists template specialization, but I want my class to accept all templates that implement a specific function, search.
Let's say I have a class A as follows:
template<class T> //add a restriction that T implements bool search(T)
class A
{
T t;
//do something that uses T.search(T x)
if(t.search(x))
//Do something
};
So basically, I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
I want to create a generic class that works for all classes that have the search functionality. Is there a way to do this?
By example, using decltype() as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A
{
};
The following is a full compiling example for a size() enabled class A
#include <string>
#include <type_traits>
template <typename T, typename = decltype(std::declval<T>().size())>
class A
{
};
int main()
{
A<std::string> as;
//A<int> ai; // compilation error
}
There is a drawback in this solution: you can hijack it explicating the second template parameter; by example, the following code compile
A<int, void> ai;
To avoid this problem you can use partial specialization as follows
template <typename T,
typename = decltype(std::declval<T>().search(std::declval<T>()))>
class A;
template <typename T>
class A<T>
{
};
Apologies in advance if this is a duplicate - I was not able to find my answer here. I have the following class:
template <typename T>
class A {
public:
using X = double;
private:
T x;
};
Now, I would like to obtain A<T>::X. Is that possible to do without declaring T?
I tried something along the lines:
template <typename T>
using B = A<T>::X;
B e = 0;
But that clearly does not work without specifying the template.
Firs, the keyword typename is needed for defining the B alias template:
template <typename T>
using B = typename A<T>::X;
The reason is that X is a dependent name: a name inside a template with a yet-to-be-determined parameter T.
Since B is an alias template taking a type template parameter (i.e.: T), that parameter has to be provided in order to determine X, for example if T is int:
B<int> e = 0;
However, note that this may not be valid or work as you expected for every T, since the A class template could have been specialized having the name X representing something completely different. For example, X could represent a type other than double:
template<>
class A<float> {
public:
using X = int; // not double!
};
The name X could represent a data member instead of a type:
template<>
class A<char> {
public:
static constexpr int X = 1; // not a type!
};
or it may not exist at all:
template<> class A { /* not name X */};
Situation
I'm trying to implement a container that holds a specific data type - let's call it C. The container(let's call it B) is an inner class of A. I'm trying to declare the template but am running into compiler issues and am not sure what I should do.
Attempts
template <typename T<C>>
class A
{
class B
{
typedef std::unique_ptr<T> containerPtr;
private:
containerPtr container;
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr; // Error!
The error is:
struct C
type name is not allowed
template <typename T,U>
class A
{
class B
{
typedef std::unique_ptr<T<U>> containerPtr;
private:
containerPtr container; // But does it contain C or some other type now?
// We have to do a check - what's the best approach?
}
}
typedef std::shared_ptr<A<std::vector<C>>> somePtr;
What is the best approach in this situation?
If you know for a fact that T is a template container storing some type C, then you don't need to specify C anywhere and can just templatize over T:
template <typename T>
class A {
class B {
/* Use the type name T however you'd like. */
};
};
This works because T has to be the name of a complete type, so if you do something like
A<std::vector<int>>
then T is std::vector<int> and any time you use T it will specifically be a std::vector of ints and not of any other type.
On the other hand, if you want the client to provide the name of a template class and then forcibly instantiate it with your choice of C, you can use template template arguments, like this:
template <template <typename...> class T>
class A {
class B {
typedef std::unique_ptr<T<C>> containerPtr;
/* ... use containerPtr ... */
};
};
This asks the user to give you a template type, so you'd write something like
A<std::vector> myObject;
and your A template will then instantiate std::vector using the type C.