pointer-to-member type and template - c++

First I got to class for this test:
struct Foo {
void test() {
std::cout << "test" << std::endl;
}
};
struct Bar {
Foo foo;
};
and a template class which accepts a pointer-to-member when constructing:
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
it works well, instantiation can be done by using test<Bar, Foo> test(bar, &Bar::foo);, except that it is boring to write Bar and Foo every time since it can be deduced for &Bar::foo, so I decide to make a specialization version:
template<typename Type, typename MemberType, MemberType Type::*p>
class test<MemberType Type::*p> {
public:
test(Type &t) {
(t.*p).test();
}
};
ok, I don't really know how to do this, I just want to pass a MemberType Type::*p as the only template parameter like this: test<&Bar::foo> test(bar);
Then I create a brand new class:
template<typename Type, typename MemberType, MemberType Type::*p>
class test2 {
public:
test2(Type &t) {
(t.*p).test();
}
};
now I can pass as a template argument rather than a constructor argument, but this time I have to write three paramters:test2<Bar, Foo, &Bar::foo> test2(bar);
So what's the right way on earth that I can simply use test<&Bar::foo> test(bar);?

You almost got it correctly.
The primary template has to use an auto template parameter (a C++17 feature):
template <auto MemberPtr> class test {};
Then the specialization:
template <typename Type, typename MemberType, MemberType Type::*p>
class test<p> // Note `p` instead of `MemberType Type::*p`.
{
public:
test(Type &t)
{
(t.*p).test();
}
};

An alternative: test t(bar, &Bar::foo);
This use your primary template:
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
and template argument deduction (TAD).
Live demo: https://coliru.stacked-crooked.com/a/52ee3ddba011473b

From C++17 onwards this is the exact intended use case for user defined deduction guides. Pre-C++17 one often used make functions for this.
#include <iostream>
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
// C++17 user defined deduction guide
template<typename Type, typename MemberType>
test(Type &t, MemberType Type::* p) -> test<std::decay_t<decltype(t)>, decltype(static_cast<std::decay<decltype(t)>>(t).*p) >;
// pre C++17 make function
template<typename Type, typename MemberType>
test<Type, MemberType> make_test(Type &t, MemberType Type::* p) {
return test<Type, MemberType>(t, p);
}
struct Foo {
void test() {
std::cout << "test" << std::endl;
}
};
struct Bar {
Foo foo;
};
int main() {
Bar b;
test<Bar, Foo> t1 (b, &Bar::foo);
// Only works in C++17
test t2(b, &Bar::foo);
auto t3 = make_test(b, &Bar::foo);
}

Related

Conditional member signature and implementation based on template type parameter

I am trying to write a template class with multiple type parameters T1 and T2. The class has a private member of type std::promise<T2>.
template <class T, class T2>
class Test
{
public:
void setValue(T2 value)
{
promise.set_value(value);
}
void setValue()
{
promise.set_value();
}
private:
std::promise<T2> promise;
};
This class compiles just fine when T2 is anything but void (as long as you don't call setValue without parameters. When T2 is void, I get a compiler error:
error C2182: 'value' : illegal use of type 'void'
When T2 is anything but void, I would like to use the first setValue method, which has a single parameter of type T2. When T2 is void, I would like to use the second setValue method, which takes no parameters. I've looked at a lot of examples, but I am relatively new to template programming, and I can't seem to make it work.
Is it possible to accomplish this with std::enable_if somehow? Or with template specialization?
helper template class specialisation:
#include <future>
template<typename T>
class TestHelper
{
public:
void setValue(T const& v)
{ promise.set_value(v); }
private:
std::promise<T> promise;
};
template<>
class TestHelper<void>
{
public:
void setValue()
{ promise.set_value(); }
private:
std::promise<void> promise;
};
template <class T, class T2>
class Test : public TestHelper<T2>
{
};
int main()
{
Test<void, int> t;
// t.setValue(); // compilation error: no matching function for call to 'std::promise<int>::set_value()'
t.setValue(0);
Test<void, void> t1;
t1.setValue();
// t1.setValue(0); // compilation error: no matching function for call to 'std::promise<void>::set_value(int)'
}
You can solve this problem with a conditional dependency on a base class:
#include <future>
#include <type_traits>
#include <iostream>
template<class T2>
struct Base {
protected:
std::promise<T2> promise;
};
template<class T2>
struct BaseWithVariable : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<typename T2>
struct BaseWithoutVariable : public Base<T2> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test
: public std::conditional<std::is_same_v<T2, void>, BaseWithoutVariable<T2>, BaseWithVariable<T2>>::type
{
};
int main()
{
Test<int, int> a;
a.setValue(5);
Test<int, void> b;
b.setValue();
}
Now you realize that you can achieve the same with specialization on the level of the intermediate class:
template<class T2>
struct BaseSetter : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<>
struct BaseSetter<void> : public Base<void> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test : public BaseSetter<T2>
{
};
And it would also not hurt, in this particular case, to omit the use of Base and just have both variants of BaseSetter use their own member variable std::promise<T2>, or std::promise<void>, respectively.
However all of these crash in runtime with GCC 7.2.0. I don't know why.

Get typename from passed in template

Is it possible to get the typename off a template that is passed into another template? Here is a basic example of what the goal is
#include <memory>
#include <string>
#include <iostream>
class IntId {
private:
int id;
public:
IntId(int id) {
this->id = id;
}
};
class StringId {
private:
std::string id;
public:
StringId(std::string id) {
this->id = id;
}
};
template<typename T_Id>
class Object : public std::enable_shared_from_this<Object<T_Id>>
{
private:
T_Id id;
public:
typedef T_Id T;
Object(T_Id id) {
this->id = id;
}
T_Id getId()
{
return this->id;
}
};
template <class T, class Enable = void>
class Observable {
public:
// Intentionally doesn't have a set so it breaks the build... I want both types to go into the value below
void setNonSpecialized(T value) {
}
};
template<typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<IntId>, T>::value>::type> {
private:
std::shared_ptr<T> value;
public:
Observable() {
value = nullptr;
};
void set(std::shared_ptr<T> newValue) {
this->value = newValue;
}
};
class UPCObject : public Object<IntId> {
};
class UserObject : public Object<StringId> {
};
int main()
{
auto upcObject = std::make_shared<UPCObject>();
auto upcObservable = std::make_shared<Observable<UPCObject>>();
upcObservable->set(upcObject); // Expected to succeed as UPCObject inherits from Object<IntId> which matches template
auto userObject = std::make_shared<UserObject>();
auto userObservable = std::make_shared<Observable<UserObject>>();
userObservable->set(userObject); // Want this to succeed as UserObject inherits from Object<StringId> which would match template Object<T::T_Id>
auto intObservable = std::make_shared<Observable<int>>();
intObservable->setNonSpecialized(0); // Expected to succeed and use the value on non-specialized Observable
return 0;
}
In the code above, upcObject succeeds in it's build because it's type matches the templated type. UserObject doesn't because it has a different Id type.
Now if I change the specialization to the following and explicitly describe the type
template <typename T, typename T_Id>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T_Id>, T>::value>::type>
I get the build error 'T_Id': template parameter not used or deducible in partial specialization 'Observable<T,std::enable_if<std::is_base_of<Object<T_Id>,T>::value,void>::type>' because T_Id isn't actually used in Observable at all.
What would be great is if I could do something like the following
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T::T_Id>, T>::value>::type>
Where I am able to get the T_Id off the type being passed in. Because in this specialization I'm checking the base of Object, it should have a type defined on it.
In your case, since you define a typedef in the Object class, you can simply do this:
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<typename T::T>, T>::value>::type>
If you were not using that typedef, you could do that:
// returning a pointer protect us from abstract types.
template<typename T>
T* get_object_type(const Object<T>&);
template<typename T>
using object_type_t = typename std::remove_pointer<
decltype(get_object_type(std::declval<const T&>()))
>::type;
And then, use the type trait:
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<object_type_t<T>, T>::value>::type>
Note that sfinae will occur first on object_type_t. If the function get_object_type is not callable using a const T&, that specialization of Observable will be ruled out. If T don't extends Object<T>, but the function get_object_type is still callable, then your condition with is_base_of will rule out the specialization of Observable.

Access type member

In my example I have a class Foo<T>. In my function test I need to get the template parameter of Foo otherwise the normal type. First I started to use std::conditional but forgot that the template parameters must all be valid, no matter which one is picked. Is the only way to create a type-specialisation for non-Foo types?
Example
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const T& a)
{
// actually I would have used !is_foo<T>::value for the first arg
// but this check is fine to minimise the example
using MY_TYPE = typename std::conditional<
std::is_same<T, int>::value,
T,
typename T::M>::type; // <---Error: error: type 'int' cannot be used prior to '::' because it has no members
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
Well you could make an UnFoo helper to get the right type for you:
template <typename T>
struct UnFoo {
using type = T;
};
template <typename T>
struct UnFoo<Foo<T>> {
using type = T;
};
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename UnFoo<T>::type; //maybe with a helper to get rid of typename
}
Another option would be to write an overload for Foo<T> and have it delegate to the other function, but that depends on what your real test function does.
You can do some void_t magic to allow SFINAE to figure help you out:
#include <type_traits>
#include <iostream>
#include <typeinfo>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// primary template handles types that have no nested ::T member:
template< class T, class = void_t<> >
struct M_or_T { using type = T; };
// specialization recognizes types that do have a nested ::T member:
template< class T >
struct M_or_T<T, void_t<typename T::M>> { using type = typename T::M; };
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename M_or_T<T>::type;
std::cout << typeid(MY_TYPE).name() << "\n";
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
What happens is that the second overload of M_or_T substitution fails for int (and for any type without a type member M) and thus the first overload is chosen. For types which have a type member M, a more specialized second overload is chosen.
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const Foo<T>& a)
{
using MY_TYPE = Foo<T>::M;
testOther<MY_TYPE>(a);
}
template <typename T>
void test(const T& a)
{
using MY_TYPE = T;
testOther<MY_TYPE>(a);
}
template <typename T, typename S>
void testOther(const S& a)
{
// do stuff
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
I'm not exactly sure what you wanted, but I hope this is what you wanted. It might be a bit off. I didn't compile this.

Is there an elegant solution to distinguish between void and non void arguments in a template member function call?

Having a base class and its specialization for void:
#include <iostream>
template <typename T>
struct Base
{
typedef T result_type;
result_type get_value() { return result_type(); }
void process_value(result_type&&) {
std::cout << "type\n";
}
};
template <>
struct Base<void>
{
typedef void result_type;
void get_value() {};
void process_value(/*void&&*/) {
std::cout << "void\n";
}
};
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
// Returning void from a function call is fine.
result_type get() { return this->get_value(); }
void invoke() {
// If T is void: error: invalid use of void expression
this->process_value(get());
}
};
int main() {
Derived<int>().invoke();
// Trigger a compilation faluure:
Derived<void>().invoke();
}
Is there an elegant solution to distinguish between void and non void arguments in the call of 'process_value' (C++11 is fine) ?
One way is to substitute some empty class for void when it comes to passing the result as an argument. I don't find it very elegant, but it works:
#include <iostream>
template <typename T>
struct Base
{
typedef T result_type;
result_type eval() { return result_type(); }
result_type get_value() { return result_type(); }
void process_value(result_type&&) {
std::cout << "type\n";
}
};
template <>
struct Base<void>
{
struct value_type { };
typedef void result_type;
value_type eval() { return value_type{}; }
void get_value() {};
void process_value(value_type) {
std::cout << "void\n";
}
};
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
result_type get() { return this->get_value(); }
void invoke() {
this->process_value(this->eval());
}
};
int main() {
Derived<int>().invoke();
Derived<void>().invoke();
}
To find something more elegant, I feel you may need to refactor your original problem.
Slightly refactored and using C++11, this is another way to approach the problem:
First, your template specialization and main() are both unchanged:
template <>
struct Base<void>
{
typedef void result_type;
void get_value() {}
void process_value( void ) {
std::cout << "void\n";
}
};
int main() {
Derived<int>().invoke();
// Trigger a compilation faluure:
Derived<void>().invoke();
}
The first significant change is in the Derived template.
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
void invoke() {
this->process_value();
}
};
As you can see, I've removed get() so that may or may not any longer match your intent. It seemed that what you were attempting to do was to create a temporary something of type T and then interrogate it. That part is unchanged, but the semantics of it are moved to the Base class which uses SFINAE to do the right thing.
I've created a convenience function is_not_void() and a templated Enable_if (after Stroustrup) to make the code a little easier to read.
#include <iostream>
#include <type_traits>
template <typename T>
constexpr bool is_not_void() {
return !std::is_void<T>::value;
}
template <bool C, typename T>
using Enable_if = typename std::enable_if<C, T>::type;
Lastly the revised Base code:
template <typename T>
struct Base
{
typedef T result_type;
template <typename U, typename = Enable_if<is_not_void<U>(), U>>
U get_value() { return U(); }
template <typename U, typename = Enable_if<is_not_void<U>(), U>>
void do_process_value( U&&) {
std::cout << "type\n";
}
void process_value() {
do_process_value(get_value<T>());
}
};
As you can see, the templates have a default second template argument which can only be instantiated if the type is not void. The effect is that they will always be matched for everything other than void types but the void specialization will be used in the case that the type is void. As you already know, even the declaration of a void && argument will cause the compilation to fail, which is why we can't use Enable_if within the argument list of process_value.
When run, this code produces:
type
void

How to specialize member functions based on class template argument

What the question says. In addition, is it possible to do this inline?
Here is a small example just to give an idea...
template<typename T>
class Foo {
public:
Foo() :z(0.0) {}
void do( const Foo<T> &f ) {
z = f.z;
}
// specialize 'do' for Foo<int>, possible inline?
private:
T z;
};
You don't need to do anything complicated. Just use overloading and delegation. Note that we cannot just add an int overload, because when T turns out to be int too, this would be an invalid overload (two functions with the same signature)
template<typename T>
class Foo {
public:
Foo() :z(0.0) {}
void doIt(const Foo<T> &f ) {
doItImpl(f);
}
private:
template<typename U>
void doItImpl(const Foo<U> &f) {
z = f.z;
}
void doItImpl(const Foo<int> &f) {
/* ... */
}
private:
T z;
};
Or, for this case, you can do this by specialization
template<typename T>
class Foo {
public:
Foo() :z(0.0) {}
void doIt(const Foo<T> &f ) {
z = f.z;
}
private:
T z;
};
template<>
inline void Foo<int>::doIt(const Foo<int> &f) {
/* ... */
}
Using specialization this way is only possible if all template arguments are fixed. In other words, partially specializing the member function is not possible.
You can sort of get this behavior by making the member function a member function template and using SFINAE (substitution failure is not an error). For example:
template <typename U>
typename std::enable_if<!std::is_integral<U>::value &&
std::is_same<T, U>::value, void>::type
f(const Foo<U>& x)
{
}
template <typename U>
typename std::enable_if<std::is_integral<U>::value &&
std::is_same<T, U>::value, void>::type
f(const Foo<U>& x)
{
}
The is_integral type trait test whether U is an integer type. If it is not, the first is instantiated; if it is, the second is instantiated.
The is_same type trait tests to ensure T and U are the same type. This is used to ensure that the member function template is not instantiated for any type other than Foo<T>.
This example makes use of the C++0x <type_traits> library; Boost also has a type traits library that you can use, which works mostly the same.
You may try to do something like this (didn't test, might not work):
template<typename T>
class Foo {
public:
Foo() :z(0.0) {}
template<typename Ty = T>
void do( const Foo<T> &f ) {
z = f.z;
}
template<>
void do<int>( const Foo<T> &f ) {
//specialized code
}
private:
T z;
};