I want to use a pointer to a class member as a template parameter as in:
template <class Class, class Result, Result Class::*Member>
struct MyStruct {
// ...
};
Using this struct like MyStruct<SomeClass, SomeResult, &SomeClass::value> variable works just fine, but I don't like that I have to specify SomeClass and SomeResult.
I would like to use MyStruct<&SomeClass::value> variable if that is possible, but without losing the ability to pass any class and have any result type.
I tried the following, but the syntax is illegal:
template <class Class, class Result>
template <Result Class::*Member>
struct MyStruct {
// ...
};
error: too many template-parameter-lists
I tried using a helper function (that does actually work in Clang but is refused by GCC):
template <class Class, class Result>
static constexpr auto makeMyStruct(Result Class::*member) ->
MyStruct<Class, Result, member> {
// ...
}
error: use of parameter `member' outside function body
error: template argument 3 is invalid
Is it possible to have a simple MyStruct<&SomeClass::value>, and if so, how?
Related question that did not solve my question:
Pointer to class member as template parameter
C++0x error with constexpr and returning template function
In c++17, with the addition of auto in template arguments (P0127), I think you can now do:
template<auto value>
struct MyStruct {};
template<typename Class, typename Result, Result Class::* value>
struct MyStruct<value> {
// add members using Class, Result, and value here
using containing_type = Class;
};
typename MyStruct<&Something::theotherthing>::containing_type x = Something();
An answer to my question was proposed in this paper for the next upcoming C++ standard:
https://isocpp.org/files/papers/n3601.html
This syntax was proposed:
template<using typename T, T t>
struct some_struct { /* ... */ };
some_struct<&A::f> x;
The need for a new syntactical construct indicates that you cannot do that by now.
I hope n3601 will be accepted. :-)
This could be a solution in C++11:
You can define the following generic type traits:
template<class T>
struct remove_member_pointer {
typedef T type;
};
template<class Parent, class T>
struct remove_member_pointer<T Parent::*> {
typedef T type;
};
template<class T>
struct baseof_member_pointer {
typedef T type;
};
template<class Parent, class T>
struct baseof_member_pointer<T Parent::*> {
typedef Parent type;
};
Now you can define an additional, 4-line wrapper macro for every struct:
template<class Class, class Result, Result Class::*Member>
struct _MyStruct {
// ...
};
#define MyStruct(MemberPtr) \
_MyStruct<baseof_member_pointer<decltype(MemberPtr)>::type, \
remove_member_pointer<decltype(MemberPtr)>::type, \
MemberPtr>
... and use it in the following way:
MyStruct(&SomeClass::value) myStruct; // <-- object of type MyStruct<&SomeClass:value>
I use this as an intermediate solution, until we switch to C++17.
Make your result class a child of your template class. assuming the pointer member is an object of your result class in public or whatever, you can access any objects by doing something like this
template <stuff for this class> :: public result
{
blah
}
Related
Can anyone tell my how to enable if/else class member template based on different derived classes from pre-defined base set? Let me use the following example:
enum class Type {
TYPEA,
TYPEB
};
// Predefined in libraries.
class BaseA {...};
class BaseB {...};
class Foo {
template <typename Derived, Type type>
void foo();
};
// User-derived
class DerivedA : public BaseA {};
class DerivedB : public BaseB {};
Normally we need two template typenames for calling the member foo.
Foo obj;
obj.foo<DerivedA, Type::TypeA>()
obj.foo<DerivedB, Type::TypeB>();
However, this native approach seems lengthy because the second template argument Type::TypeA and Type::TypeB can obviously be deduced by compiler through the first argument DerivedA and DerivedB, if they are derived from pre-defined base properly. I notice that c++11 provides is_base_of template but I am not sure how to use it in my case. To be more specific, below is the expected solution:
obj.foo<DerivedA>(); // Automatically deduce type = Type::TypeA
obj.foo<DerivedB>(); // Automatically deduce type = Type::TypeB
And if the compile fails to deduce the Type from the first typename, it should it just goes back to the normal declaration obj.foo<MyClass, MyType> where MyType is either Type::TypeA or Type::TypeB.
Sounds like you just want a default template argument:
class Foo {
template <typename Derived, Type type = get_type_from<Derived>::value>
void foo();
};
Where get_type_from<> is a metafunction to be filled in later based on how you actually figure out the Types.
template<Type t>
using etype_tag = std::integral_constant<Type, t>;
template<class T>
struct tag_t {
using type=T;
template<class D,
std::enable_if_t<std::is_base_of<T, D>::value, int>* =nullptr
>
constexpr tag_t( tag_t<D> ) {}
constexpr tag_t() = default;
constexpr tag_t(tag_t const&) = default;
};
template<class T>
constexpr tag_t<T> tag{};
constexpr etype_tag<Type::TYPEA> get_etype( tag_t<BaseA> ) { return {}; }
constexpr etype_tag<Type::TYPEB> get_etype( tag_t<BaseB> ) { return {}; }
template<class T>
constexpr decltype( get_etype( tag<T> ) ) etype{};
Now etype<Bob> is a compile-time constant integral constant you want.
class Foo {
template <typename Derived, Type type=etype<Derived>>
void foo();
};
makes the 2nd argument (usually) redundant.
You can extend get_etype with more overloads in either the namespace where etype is declared, or in the namespace of tag_t, or in the namespace of the type you are extending get_etype to work with, and etype will automatically gain support (assuming it is used in a context where the extension is visible: failure of that requirement leaves your program ill formed).
Live example
I am trying to do the following: a templated class should provide some functions dependend on whether or not the type it has been templated with contains a member variable with a given name. As example the following pseudocode which should provide "printid()" only when templated struct/class has a member called "id":
#include <iostream>
#include <type_traits>
struct A { int id; };
struct B { };
template<typename T>
class foo
{
T myvar;
public:
#if exists T.id (or the alternative: #if exists myvar.id)
printid() { std::cout << "I have element id."; }
#endif
};
int main(){
foo<A> ok;
ok.printid(); // should compile and execute
foo<B> nok;
nok.printid(); // should not compile
return 0;
}
Digging around SFINAE, traits, std::enable_if and StackOverflow, I think it can be done ... somehow. But I somehow fail to combine enable_if with the the following snippet from the question How to detect whether there is a specific member variable in class?:
template<typename T, typename = void>
struct has_id : std::false_type { };
template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };
Any help appreciated.
Yep, it's possible. Here's an example:
template<typename T>
class foo
{
T myvar;
public:
template <class _T = T,
class = typename std::enable_if<
!std::is_function<decltype(_T::id)>::value>
::type>
void printid() { std::cout << "I have element id."; }
};
Specifically, note how we're "taking in" T as _T in order to not force a constraint on the class template parameter (which would make the class itself un-compileable). Instead, we're creating a new, independent template member function, which doesn't force anything on T itself—it just "happens to" use it as a default argument. That's the key part.
I have a class where I want the template parameter B to have a default type. The problem is that the default type is a complicated expression depending also on the type of A.
The following code illustrates the situation but does obviously not compile, because defaultB type is not know inside the template expression.
template<class A, class B = defaultB>
class Foo{
typedef A::Bar Bar;
typedef Bar::Ex defaultB;
};
Does anybody have an idea how to solve this problem properly?
You could maintain a namespace of defaults like this:
namespace detail {
template <typename A>
using defaultB = typename A::Bar::Ex;
}
template<class A, class B = typename detail::defaultB<A>>
class Foo{
};
This lets you have as complex expressions as you like in your detail namespace without making the Foo declaration ugly.
An alternative to TartanLlama's excellent suggestion is to maintain a dummy type hierarchy to lift up the typedef's into scope:
template<class A>
struct _Foo {
typedef typename A::Bar Bar;
typedef typename Bar::Ex defaultB;
};
template<class A,class B=typename _Foo<A>::defaultB>
struct Foo : _Foo<A> {
};
I want to use a pointer to a class member as a template parameter as in:
template <class Class, class Result, Result Class::*Member>
struct MyStruct {
// ...
};
Using this struct like MyStruct<SomeClass, SomeResult, &SomeClass::value> variable works just fine, but I don't like that I have to specify SomeClass and SomeResult.
I would like to use MyStruct<&SomeClass::value> variable if that is possible, but without losing the ability to pass any class and have any result type.
I tried the following, but the syntax is illegal:
template <class Class, class Result>
template <Result Class::*Member>
struct MyStruct {
// ...
};
error: too many template-parameter-lists
I tried using a helper function (that does actually work in Clang but is refused by GCC):
template <class Class, class Result>
static constexpr auto makeMyStruct(Result Class::*member) ->
MyStruct<Class, Result, member> {
// ...
}
error: use of parameter `member' outside function body
error: template argument 3 is invalid
Is it possible to have a simple MyStruct<&SomeClass::value>, and if so, how?
Related question that did not solve my question:
Pointer to class member as template parameter
C++0x error with constexpr and returning template function
In c++17, with the addition of auto in template arguments (P0127), I think you can now do:
template<auto value>
struct MyStruct {};
template<typename Class, typename Result, Result Class::* value>
struct MyStruct<value> {
// add members using Class, Result, and value here
using containing_type = Class;
};
typename MyStruct<&Something::theotherthing>::containing_type x = Something();
An answer to my question was proposed in this paper for the next upcoming C++ standard:
https://isocpp.org/files/papers/n3601.html
This syntax was proposed:
template<using typename T, T t>
struct some_struct { /* ... */ };
some_struct<&A::f> x;
The need for a new syntactical construct indicates that you cannot do that by now.
I hope n3601 will be accepted. :-)
This could be a solution in C++11:
You can define the following generic type traits:
template<class T>
struct remove_member_pointer {
typedef T type;
};
template<class Parent, class T>
struct remove_member_pointer<T Parent::*> {
typedef T type;
};
template<class T>
struct baseof_member_pointer {
typedef T type;
};
template<class Parent, class T>
struct baseof_member_pointer<T Parent::*> {
typedef Parent type;
};
Now you can define an additional, 4-line wrapper macro for every struct:
template<class Class, class Result, Result Class::*Member>
struct _MyStruct {
// ...
};
#define MyStruct(MemberPtr) \
_MyStruct<baseof_member_pointer<decltype(MemberPtr)>::type, \
remove_member_pointer<decltype(MemberPtr)>::type, \
MemberPtr>
... and use it in the following way:
MyStruct(&SomeClass::value) myStruct; // <-- object of type MyStruct<&SomeClass:value>
I use this as an intermediate solution, until we switch to C++17.
Make your result class a child of your template class. assuming the pointer member is an object of your result class in public or whatever, you can access any objects by doing something like this
template <stuff for this class> :: public result
{
blah
}
I'm trying to do something like this (completely synthetic example, because the real code is a bit to convoluted):
enum MyInfoType
{
Value1, Value2
};
template<typename T> struct My_Type_Traits
{};
template<> struct My_Type_Traits<int>
{
typedef MyInfoType InfoType;
};
template<typename T>
class Wrap
{
template<My_Type_Traits<T>::InfoType INFO> int GetInfo()
{...}
};
...
Wrap<int> w;
int info = w.GetInfo<Value1>();
So basically I'm trying to use a typedef from inside another struct as type of a template parameter. With this code however the compiler complains that struct My_Type_Traits<T>::InfoType is not a type. So what do I need to do to make this work?
You need to use the typename keyword: like typename My_Type_Traits<T>::InfoType to let the compiler know you're referring to a nested type.