I would like to have access to a nested class using templates, and can't figure out how to do it: A sample code:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> class mysubthing { typedef subT value_type; };
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X; // a container for all value_types
#ifdef MAKE_IT_FAIL
// this should automatically set the X<mything<T>::mysubthing<subT>
template<typename T,typename subT> struct X<typename mything<T>::template mysubthing<subT>> {
using value_type = subT;
};
#endif
typedef mything<int> intthing;
#ifndef MAKE_IT_FAIL
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
#endif
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
This compiles without -DMAKE_IT_FAIL, but of course it completely misses the point about templates, since what I wanted was entered manually. How can I make it work with -DMAKE_IT_FAIL?
You can't specialize like that:
template<typename T,typename subT>
struct X<typename mything<T>::template mysubthing<subT>> {
since deduction of T from types like outer<T>::anything_after is impossible (not supported) in C++.
You don't really need specialization in this general case at all. Just define the default X, and then only specialize the other cases:
template <typename T> class mything {
typedef unsigned key;
T data;
public:
template <typename subT> struct mysubthing
{
typedef subT value_type;
};
using subthing = mysubthing<T>;
using subthing_ro = mysubthing<const T>;
};
template<typename T> struct X
{
using value_type = typename T::value_type;
};
// this should automatically set the X<mything<T>::mysubthing<subT>
typedef mything<int> intthing;
template<> struct X<mything<int>::subthing> { using value_type = int; };
template<> struct X<mything<int>::subthing_ro> { using value_type = const int; };
int main(void) {
intthing t;
X<intthing::subthing>::value_type data = 1; // a data object
X<intthing::subthing_ro>::value_type data_ro = 1; // read-only data object
return 0;
}
Addendum
According to one of the comments, X is actually std::iterator_traits, which is already defined. In that case, the only way around it is to define the iterator class outside the mything class:
template <typename T, typename subT>
class mything_iterator {
typedef subT value_type;
};
template <typename T> class mything {
typedef unsigned key;
T data;
public:
using iterator = mything_iterator<T, T>;
using const_iterator = mything_iterator<T, const T>;
};
namespace std {
template<typename T, class subT>
class iterator_traits<mything_iterator<T, subT>>{
using value_type =typename mything_iterator<T, subT>::value_type;
// etc...
};
template<> struct iterator_traits<mything<int>::iterator>
{ using value_type = int; };
template<> struct iterator_traits<mything<int>::const_iterator>
{ using value_type = int; };
}
Related
What I want to do is to create a generic traits class with the default logic and then write code to specialize each specific case with only the things that are different from the generic case. My goal is to remove code duplication and avoid writing unnecessary code.
I'll give you an example:
int genericFunction(); // defined somewhere else
int specialFunction(); // defined somewhere else
template<int id>
struct IdTraits
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
template<>
struct IdTraits<1>
{
// Using MyType and AnotherType of IdTraits generic case [how?]
static constexpr auto&& f = specialFunction;
};
template<>
struct IdTraits<2>
{
// Using MyType and f of IdTraits generic case [how?]
using AnotherType = char;
};
template<int id, class Traits = IdTraits<id>>
struct General
{
void foo(int arg)
{
Traits::MyType myType;
Traits::AnotherType anotherType;
Traits::f(arg);
// Do stuff with myType and anotherType
}
};
Do you think is theoretically possible to do something like this?
You can have a second Trait to use instead that will do this work. Its purpose will be to check the presence of each element inside the current IdTrait<id> and set the default type/function if not.
Using current experimental detection for the two types, and member getF for the function:
template<int id>
struct MyIdTraits {
template <typename T> using MyType_t = typename T::MyType;
using MyType = std::experimental::detected_or_t<int, MyType_t, IdTraits<id>>;
template <typename T> using AnotherType_t = typename T::AnotherType;
using AnotherType = std::experimental::detected_or_t<double, AnotherType_t, IdTraits<id>>;
template <typename T, typename = decltype(T::f)>
static constexpr auto getF(int) { return T::f; }
template <typename T>
static constexpr auto getF(unsigned) { return genericFunction; }
static constexpr auto&& f = getF<IdTraits<id>>(42);
};
Then replace your trait by this one:
template<int id, class Traits = MyIdTraits<id>>
struct General { ... };
Demo
Yes. Put your generic case in a base class:
namespace details
{
struct IdTraits_generic
{
using MyType = int;
using AnotherType = double;
static constexpr auto&& f = genericFunction;
};
}
template<int id> struct IdTraits : details::IdTraits_generic
{
};
template<> struct IdTraits<1> : details::IdTraits_generic
{
static constexpr auto&& f = specialFunction;
};
template<> struct IdTraits<2> : details::IdTraits_generic
{
using AnotherType = char;
};
What I need:
template <class Context/*if Context has a Buffer typedef*/>
struct Buffer {
typedef typename Context::Buffer type;
};
template <class Context/*if Context doesn't have a Buffer typedef*/>
struct Buffer {
typedef std::shared_ptr<void> type;
};
If the parameter class Context has a Buffer typedef, then it's used, otherwise shared_ptr would be used.
How to write the templates? thanks in advance.
This is fairly straightforward using a void_t helper:
#include <memory>
#include <type_traits>
template <typename...> using void_t = void;
template <class Context,class = void>
struct Buffer {
typedef std::shared_ptr<void> type;
};
template <class Context>
struct Buffer<Context,void_t<typename Context::Buffer> > {
typedef typename Context::Buffer type;
};
int main()
{
struct Context1 {
};
struct Context2 {
typedef int Buffer;
};
{
using A = Buffer<Context1>::type;
using B = std::shared_ptr<void>;
static_assert(std::is_same<A,B>::value,"");
}
{
using A = Buffer<Context2>::type;
using B = int;
static_assert(std::is_same<A,B>::value,"");
}
}
Note that std::void_t is in C++17, but it is easy enough to create your own.
Use partial class template specializations with SFINAE. It works even with C++11.
(Live example)
struct A
{
using tag = void;
static constexpr const char *name = "A";
};
struct B
{
static constexpr const char *name = "B";
};
template <typename T> struct S
{
static void func()
{
std::cout << T::name << " - no tag\n";
};
};
template <typename T, typename = typename T::tag> using enable_if_has_tag = T;
template <typename T> struct S<enable_if_has_tag<T>>
{
static void func()
{
std::cout << T::name << " - has tag\n";
};
};
int main()
{
S<A>::func(); // -> `A - has tag`
S<B>::func(); // -> `B - no tag`
}
This is possible:
struct A {
//void f(); < not declared in struct A
};
template<typename T>
struct Wrapper {
T t;
void call_f() { t.f(); }
};
int main() {
Wrapper<A> w;
}
This compiled fine, as long as w.call_f() is not called. w.call_f() can not be instantiated because A::f does not exist.
I'm having a situation with such a wrapper template that gets used with different T types, which do not always implement all parts of the interface. (Mainly to avoid code duplication).
This does not work:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
using its_i_type = typename T::i_type;
// compile error, even if `i_type` gets never used
};
int main() {
Wrapper<A> w;
}
neither does this:
struct A {
//using i_type = int; < not declared/defined
};
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
int main() {
Wrapper<A> w;
}
Is there a good way to handle these situations, without a lot of code duplication (like a specialization for Wrapper, etc.)?
You can defer the type deduction of its_i_type. Basically, you create a simple wrapper that you must go through.
To extend it to other types you require, (I wanted to suggest type_traits-like solution, but since you don't want specializations) you could define all the types you need:
template<typename T>
struct Wrapper {
private:
template<typename U> struct i_typper { using type = typename U::i_type; };
template<typename U> struct k_typper { using type = typename U::k_type; };
template<typename U> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T>;
using k_trait = k_typper<T>;
using p_trait = p_typper<T>;
};
Example:
struct A { using i_type = int; };
struct B { using i_type = int; using k_type = float; };
int main() {
Wrapper<A> w; //Works now.
Wrapper<A>::i_trait::type mk1; //Works
Wrapper<A>::k_trait::type mk2; //Fails, not defined
Wrapper<B>::i_trait::type mk3; //Works
Wrapper<B>::k_trait::type mk4; //Works
}
For the case of:
template<typename T>
struct Wrapper {
typename T::i_type call_f() { return 0; }
// does not compile, even if `call_f()` is never instantiated
};
You have few options here:
make that function a member function template
Use some form of type_traits mechanism, which will still involve specialization
Go the way of abstracting common Wrapper stuff in a base class WrapperBase;
For the first option, you'll have to modify it a bit to further defer deduction
template<typename T>
struct Wrapper {
private:
template<typename U, typename> struct i_typper { using type = typename U::i_type; };
template<typename U, typename> struct k_typper { using type = typename U::k_type; };
template<typename U, typename> struct p_typper { using type = typename U::p_type; };
public:
using i_trait = i_typper<T, void>;
using k_trait = k_typper<T, void>;
using p_trait = p_typper<T, void>;
template<typename U = void>
typename k_typper<T, U>::type call_f() { return 0; }
};
I'll leave the second option as an exercise: (it may end up being something like:
template<typename T>
struct wrapper_traits {
....
};
template<>
struct wrapper_traits<A>{
using ....
};
template<typename T>
struct Wrapper {
....
public:
using i_trait = wrapper_traits<T>;
using k_trait = wrapper_traits<T>;
using p_trait = wrapper_traits<T>;
};
Jarod's answer is simpler. But this will work if you do not have access to std::experimental, or your company code policy forbids you...
With std::experimental::is_detected, you may do
template<typename T>
using i_type_t = typename T::i_type;
template<typename T>
struct Wrapper {
using its_i_type = typename std::experimental::detected_t<i_type_t, T>;
// would be T::i_type or std::experimental::nonesuch
};
Or to better handle case, something like:
template<typename T, bool = std::experimental::is_detected<i_type_t, T>::value>
struct WrapperWithIType {
// Empty for false case.
};
template<typename T>
struct WrapperWithIType<T, true> {
using its_i_type = i_type_t<T>;
its_i_type call_f() { return 0; }
};
and then
template<typename T>
struct Wrapper : WrapperWithIType<T> {
// Common stuff
};
I have a class template which can be passed a Class or Class pointer.
/* Template specialization hack to determine if type is a pointer */
struct type_true { };
struct type_false { };
template <class PRT>
class is_pointer : public type_false {
};
template <class PRT>
class is_pointer <PRT * > : public type_true {
};
template <typename T>
class MyClass {
//Return an new instance allocated on stack
T new_instance(type_false n_ptr) {
T new_obj;
//Init stuff
return new_obj;
}
//Return an new instance allocated on heap
T new_instance(type_true is_ptr) {
T new_obj = new T();
//Init stuff
return new_obj;
}
};
Compilation fails with the following error:
cannot convert 'Class**' to 'Class*' in initialization
I think this is because T is already a pointer new T() thinks i want to allocate a pointer to a pointer. e.g.
OtherClass * new_obj = OtherClass*new();
Is there some way i can strip the * from the T type or another solution?
Thanks
Ben
Is there some way i can strip the * from the T type or another solution?
Of course, you can.
Use this: (it removes just one degree of pointerness, i.e it makes T* -> T, and T** -> T*, etc)
template<typename T>
struct remove_pointer
{
typedef T type;
};
template<typename T>
struct remove_pointer<T*>
{
typedef T type;
};
Then,
typedef typename remove_pointer<T>::type type;
T new_obj = new type();
If you want to make T*** -> T i.e remove all *, then replace the above specialization with this:
template<typename T>
struct remove_pointer<T*>
{
typedef typename remove_pointer<T>::type type;
};
Or use this, to remove any level of indirection from the type.
template<typename T> struct stripptr {
typedef T thetype;
};
template<typename T> struct stripptr<T *> {
typedef T thetype;
};
template <typename T> struct MyClass {
static T create() {
T new_obj;
return new_obj;
}
};
template <typename T> struct MyClass<T *> : MyClass<T> {
};
This code compiles with VS (/za) but doesn't compile with GCC. Who is right and who is wrong? Or both are wrong/right?
#include <iostream>
#include <type_traits>
using namespace std;
struct NullType
{
//NullType(){}
template<class T>
NullType(T value){}
enum {value};
};
template<class T>
struct IntType
{
typedef T type;
};
template<int value_>
struct Low
{
typedef int type;
enum {value = value_};
};
template< class T>
struct Low_impl
{
protected:
T value_;
Low_impl():value_(T()){/*e.b.*/}
Low_impl(T value):value_(value){/*e.b.*/}
};
template<class T>
struct isNullType
{
enum {value = false};
};
template<>
struct isNullType<NullType>
{
enum {value = true};
};
template<class T>
struct TypeTraits
{
typedef T type;
};
/*template<>
struct TypeTraits<int>
{
typedef int type;
};*/
template<class Int_>
struct Int_Type_Tag
{
static_assert(std::is_integral<Int_>::type,"Non Integral Type Is ILLEGAL As a Parameter to this class ");
typedef Int_ type;
};
template<class T>
struct TypeTraits<Int_Type_Tag<T>>
{
typedef typename Int_Type_Tag<T>::type type;
};
template<class Int_Type,class L = NullType>
struct Int : private std::conditional<isNullType<L>::value,
NullType,
Low_impl<typename TypeTraits<Int_Type>::type>>::type
{
typedef typename std::conditional<isNullType<L>::value,
NullType,
Low_impl<typename TypeTraits<Int_Type>::type>>::type BaseType;
Int():BaseType(L::value){}
};
int main()
{
Int<int> a;
cout << sizeof(a);
return 0;
}
Error from GCC 4.5.1
error: no matching function for call to 'NullType::NullType(NullType::&)'|
You need to instantiate NullType::NullType(T) with the type of an anonymous enum NullType::value.
This was allowed by N2657, which is implemented in gcc 4.5.