C++ enable_if in class - different ways of destruction - c++

I am developing an application for an embedded device and therefore I do not have type_traits and enable_if (poor compiler quality). I created them on my own:
template <typename T>
struct is_pointer_t{
enum{value = false};
};
template <typename T>
struct is_pointer_t<T*>{
enum{value = true};
};
and simillar declarations for consts and volatiles.
Now implementation of enable_if:
template <bool boolean, typename T = void>
struct enable_if{};
template <typename T>
struct enable_if<true, T>{
typedef T type;
};
Now I want to have a class, that depeneding on wheather I use pointers or normal types it calls destructor on them or not, so it would be great if I could have templated destructor. But I do not know how to do this as I am just beginning to undestand template programming. Following attempts failed:
template <typename T>
class pointerOrNot{
public:
template <typename U>
void one();
};
template <typename T>
template <typename enable_if<is_pointer_t<T>::value>::type>
void pointerOrNot<T>::one(){
std::cout << "Success1" << std::endl;
}
template <typename T>
template <typename enable_if<!is_pointer_t<T>::value>::type>
void pointerOrNot<T>::one(){
std::cout << "Success2" << std::endl;
}
and it says it does not match definition. So I tried following:
template <typename T>
class pointerOrNot{
public:
template <typename enable_if<is_pointer_t<T>::value>::type>
void one();
template <typename enable_if<!is_pointer_t<T>::value>::type>
void one();
};
But then one of one() has empty type as template and compilation fails. How can I do this? Also is it possible to do this with destructor?

First lets consider the following class for pointers:
template <typename T>
struct Introducer{
void intro(){
std::cout<<"I'm a pointer, I point to adress "<<mem<<std::endl;
}
T mem;
Introducer(T m):mem(m){}
};
it works fine with pointers, but it also works with non-pointers:
Introducer<int> i(10);
i.intro();//just fine!
We would like to detect this misuse during the compile time, also we change Introducer to
template <typename T>
struct Introducer{
typename enable_if<is_pointer_t<T>::value, void>::type //this is the return type of the function
intro(){
std::cout<<"I'm a pointer, I point to adress "<<mem<<std::endl;
}
...
};
Now compiler does not allow us to use non-pointer with Introducer. In the next step we would like to have a special function for non-pointers by means of SFINAE:
template <typename T>
struct Introducer{
typename enable_if<is_pointer_t<T>::value, void>::type
intro(){
std::cout<<"I'm a pointer, I point to adress "<<mem<<std::endl;
}
typename enable_if<!is_pointer_t<T>::value, void>::type
intro(){
std::cout<<"I'm a non-pointer, my value is "<<mem<<std::endl;
}
...
};
Hell, it does not even compile! Lets read the passage about SFINAE more carefully:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed if written using the substituted arguments. Only invalid
types and expressions in the immediate context of the function type
and its template parameter types can result in a deduction failure.
T is not in the immediate context and thus SFINAE doesn't work, lets bring T into the immediate context:
template <typename T>
struct Introducer{
template <typename C=T>
typename enable_if<is_pointer_t<C>::value, void>::type
intro(){
std::cout<<"I'm a pointer, I point to adress "<<mem<<std::endl;
}
template <typename C=T>
typename enable_if<!is_pointer_t<C>::value, void>::type
intro(){
std::cout<<"I'm a non-pointer, my value is "<<mem<<std::endl;
}
...
};
now the program
int main(){
Introducer<float*> fp(NULL);
fp.intro();
//But this works also:
Introducer<int> i(10);
i.intro();
}
results in:
I'm a pointer, I point to adress 0
I'm a non-pointer, my value is 10
What about the destructor? The easiest way would be to call a SFINAE-destruction-function from the destructor (I never saw a SFINAE-destructor and would not know how to write one, but this doesn't mean anything):
template <typename T>
struct Introducer{
...
~Introducer(){
intro();
std::cout<<"and I'm deleted..."<<std::endl;
}
};

Related

How to declare a variable whose type is the generics of another object?

Consider the following piece of C++ code:
int main(){
MyObject<int> obj;
foo(obj);
}
template <typename T>
void foo(T& objct){
...
}
In foo, the type of objct will be MyObject<int>.
I would like to create a variable in foo() whose type is the objct's generics, in this case, int.
Is there a way to do that? Thank you.
Edit
Unfortunately (I think) I can't rewrite the signature because the function foo() is called with different type of objects, for example
int main(){
MyObject<int> obj;
MyDifferentObject<int> obj2;
foo(obj);
foo(obj2);
}
What about defining foo() using a template-template parameter?
template <template <typename...> class C, typename T>
void foo (C<T> & objct)
{
/...
}
or also
template <template <typename...> class C, typename T, typename ... Ts>
void foo (C<T, Ts...> & objct)
{
/...
}
to be more flexible and accept also type with multiple template types parameters.
This way, if you call
MyObject<int> obj;
MyDifferentObject obj2;
foo(obj);
foo(obj2);
you have that C is MyObject in first case, MyDifferentObject in the second case and T is int in both cases.
This, obviously, works only if the argument of foo() are object of a template class with only template type parameters so, for example, doesn't works for std::array
std::vector<int> v;
std::array<int, 5u> a;
foo(v); // compile: only types parameters for std::vector
foo(a); // compilation error: a non-type template parameter for std::array
I would like to create a variable in foo() whose type is the objct's generics, in this case, int.
Is there a way to do that?
If you can change the function signature, then you can do this:
template <typename T>
void foo(MyObject<T>& objct){
T variable;
If that is not an option, for example if you want foo to allow other templates too (such as in your edited question), then you can define a type trait:
template<class T>
struct fancy_type_trait
{
};
template<class T>
struct fancy_type_trait<MyObject<T>>
{
using type = T;
};
template<class T>
struct fancy_type_trait<MyDifferentObject<T>>
{
using type = T;
};
template <typename T>
void foo(T& objct){
using V = typename fancy_type_trait<T>::type;
V variable;
You can write a trait that determines the first template parameter of any instantiation of a template with one template parameter:
#include <type_traits>
template <typename T>
struct MyObject {};
template <typename T>
struct MyOtherObject {};
template <typename T>
struct first_template_parameter;
template <template<typename> typename T,typename X>
struct first_template_parameter< T<X> > {
using type = X;
};
int main() {
static_assert(std::is_same< first_template_parameter<MyObject<int>>::type,
first_template_parameter<MyOtherObject<int>>::type>::value );
}
The trait first_template_parameter can take any instantiation of a template with a single parameter and tells you what that parameter is. first_template_parameter< MyObject<int> >::type is int. More generally first_template_parameter< SomeTemplate<T> >::type is T (given that SomeTemplate has one parameter).
This is a slight generalization of the trait used in this answer and if needed it could be generalized to also work for instantiations of tempaltes with more than one parameter.
In your function you would use it like this:
template <typename T>
void foo(T& objct){
typename first_template_parameter<T>::type x;
}

Function template partial specialization - are there any workaround?

I have the following function
enum class NodeCachingOptions
{
AddPath,
DontAddPath
};
template <typename T, NodeCachingOptions>
T* CreateSObject(const MPath& path)
Idea was to specialize function for different NodeCachingOptions.
Turned out it is impossible to use partial function template specialization, thus I tried a workaround:
template <typename T, NodeCachingOptions>
T* CreateSObject(const MPath& ob)
{
CreateSObject_Impl<class T, NodeCachingOptions> temp
return temp.call(ob);
}
template <typename T, NodeCachingOptions>
struct CreateSObject_Impl
{
T* call(const MPath& ob);
};
template <typename T>
struct CreateSObject_Impl<typename T, NodeCachingOptions::AddPath>
{
T* call(const MDagPath& ob)
{…}
}
template <typename T>
struct CreateSObject_Impl<typename T, NodeCachingOptions::DontAddPath>
{…}
However I'm getting compile error: ::NodeCachingOptions': illegal type for non-type template parameter '__formal'
What am I doing wrong and is there a better way to solve this problem?
I took idea of struct impl from here: Partial template specialization of free functions - best practices
Your syntax is all wrong. Make it
template <typename T, NodeCachingOptions opt>
T* CreateSObject(const MPath& ob)
{
CreateSObject_Impl<T, opt> temp;
return temp.call(ob);
}
You pass the value of type NodeCachingOptions as the second template paramter of CreateSObject_Impl, not the type itself.
You may want to make call a static member of CreateSObject_Impl, and write return CreateSObject_Impl<T, opt>::call(ob);

function implementation with enable_if outside of class definition

So basically, I have a very basic generic class for now, currently testing the type_traits header. I am currently trying to make a function to work with certain types i.e arithmetic ones for now.
#include <type_traits>
template <typename T> class Test {
public:
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
The function works perfectly and for arithmetic types only.
But I like to keep my classes tidy and only have them have prototypes, while the function implementations are outside of the class.
With standard templates i.e
void test();
template <typename T> void Test<T>::test() {}
It is simple and I know how to, but I have no clue how to declare the implementation outside of the class with "std::enable_if" and every attempt I have made, during compilation says that that the prototype does not match any in the class.
I have managed to find a similar question here but the class there is standard and not generic.
PS. I am using MinGW-w64 with -std=c++17
You need one set of template parameters for the class template and one separate set of template parameters for the member function template. You need to repeat the entire complicated return type, since it's part of the function template signature. And note you cannot repeat the default argument =T, or the compiler will think you're trying to define it twice (without checking whether or not the new definition is identical).
template <typename T> template <typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type
Test<T>::print()
{
// Implementation here.
}
By the way, you're using the "long way" of writing the type, as was needed in C++11. But C++14 introduced a std::enable_if_t shortcut, and C++17 introduced a std::is_arithmetic_v shortcut. So if you're using C++17, you can also write the type
typename std::enable_if<std::is_arithmetic<U>::value>::type
as just
std::enable_if_t<std::is_arithmetic_v<U>>
If you put the enable_if in the default template parameter, which is imo nicer anyway, the out-of-class definition becomes a bit easier:
template<typename T>
struct Test
{
template <typename S = T
, typename = typename std::enable_if<std::is_arithmetic<S>::value>::type >
void print();
};
template<typename T>
template<typename S, typename>
void Test<T>::print()
{
//some code
}
You can try with
template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
{ /* do something */ }
The following is a full working example
#include <iostream>
#include <type_traits>
template <typename T> class Test
{
public:
template <typename U = T>
std::enable_if_t<std::is_arithmetic<U>::value> print();
};
template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
{ std::cout << "test!" << std::endl; }
int main ()
{
Test<int> ti;
Test<void> tv;
ti.print(); // compile
//tv.print(); // compilation error
}
Off Topic 1
Observe that your solution can be hijacked in this way
Test<void>{}.print<int>();
To avoid this problem you could impose that T is equal to U,
template <typename T> class Test
{
public:
template <typename U = T>
std::enable_if_t< std::is_arithmetic<U>::value
&& std::is_same<T, U>::value> print()
{ }
};
Off Topic 2
As you can see, you have to repeat the SFINAE part (std::enable_if_t, std::is_arithmetic and std::is_same).
Taking in count that you have to repeat the implementation in an header, I don't think (IMHO) that to write the implementation of template classes outside the body of the class is a great idea.
Since you haven't posted what you attempted I can't tell you where you went wrong. But this is how you would implement the member function outside the class definition (although it still needs to be implemented in the header, so I don't think this is worth the trouble)
template <typename T> class Test {
public:
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
template <typename T> // class template parameter
template <typename U> // function template parameter
inline typename std::enable_if<std::is_arithmetic<U>::value>::type Test<T>::print()
{
}
Live demo
template<typename T>
struct test
{
template<typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
template<typename T> template<typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type test<T>::print()
{
}
void foo()
{
test<int> t;
t.print();
test<void*> u;
u.print();
}
If you need an extra template parameter U, as the other answers explained the right syntax is
template<typename T>
struct test
{
template<typename U>
... a_method(...);
};
template<typename T>
template<typename U>
... test<T>::a_method(...)
{
...
}
However in your peculiar case, if you only need to check some properties of the T type this is really an extra complication. Introduction of the U type is "artificial" and is only here because of the SFINAE
IMHO, it is much more elegant and simpler to use if constexpr
#include <iostream>
#include <type_traits>
template <typename T>
class Test
{
public:
void print();
};
template <typename T>
void Test<T>::print()
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "\nOk T is arithmetic";
// ... your implementation here ...
}
else
{
// throw an exception or do what ever you want,
// here a compile-time error
static_assert(!std::is_same_v<T, T>, "not implemented yet...");
}
}
main()
{
Test<int> t;
t.print();
Test<void> t2;
// t2.print(); <- will generate a compile time error
}

A template that accepts only pointer type arguments

After seeing that a template can be partially specialized for reference or pointer types, I was wondering whether I can write a template that accepts only a pointer type to start with. This is my attempt:
template <typename T*>
struct MyTemplate{};
int main() {
MyTemplate<int *> c;
(void)c;
return 0;
}
This does not compile. How should it be modified? (i.e. if what I am trying to accomplish is at all possible)
You may use partial specialization:
template <typename T> struct MyTemplate; // Declaration
template <typename T> struct MyTemplate<T*> // Specialization
{
};
or use static_assert
template <typename T> struct MyTemplate
{
static_assert(std::is_pointer<T>::value, "Expected a pointer");
// T = value_type*
using value_type = std::remove_pointer_t<T>;
};

Templated class specialization where template argument is a template

I wondering if something similar to this is possible. Basically, I have a templated class that occasionally takes objects of templated classes. I would like to specialize it (or just a member function)for a specific templated class, but the 'generic' form of that class.
template<typename T, typename S>
class SomeRandomClass
{
//put something here
};
template<typename T>
class MyTemplateClass
{
void DoSomething(T & t) {
//...something
}
};
template<>
void MyTemplateClass< SomeRandomClass<???> >::DoSomething(SomeRandomClass<???> & t)
{
//something specialized happens here
}
Replacing the question marks with appropriate types (double, etc) works, but I would like it to remain generic. I don't know what to put there, as any types wouldn't have been defined. I've looked around, and learned about template template parameters, and tried various combinations to no avail. Thanks for the help!
It's possible to specialize the class like this
template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
void DoSomething(SomeRandomClass<T,S>& t) { /* something */ }
};
It's not possible to specialize just the member method, because the specialization is on the class as a whole, and you have to define a new class. You can, however, do
template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
void DoSomething(SomeRandomClass<T,S>& t);
};
template <>
template <typename T,typename S>
void MyTemplateClass<SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S>& t)
{
// something
}
to split up the declaration and definition.
I'm not completely sure why #Ryan Calhoun specialized the way he did but here's a more terse example:
// class we want to specialize with later on
template<typename T, typename S>
struct SomeRandomClass
{
int myInt = 0;
};
// non-specialized class
template<typename T>
struct MyTemplateClass
{
void DoSomething(T & t)
{
std::cout << "Not specialized" << std::endl;
}
};
// specialized class
template<typename T, typename S>
struct MyTemplateClass< SomeRandomClass<T, S> >
{
void DoSomething(SomeRandomClass<T,S> & t)
{
std::cout << "Specialized" << std::endl;
}
};
You can see that you don't need the redundant syntax used in the accepted answer:
template<>
template<typename T, typename S>
Working Demo
Alternative
You can use type_traits and tag-dispatch within your non-specialized class to specialize just the function.
Let's first make a concept for is_random_class:
// concept to test for whether some type is SomeRandomClass<T,S>
template<typename T>
struct is_random_class : std::false_type{};
template<typename T, typename S>
struct is_random_class<SomeRandomClass<T,S>> : std::true_type{};
And then let's declare our MyTemplateClass again, but this time not templated (because we're not specializing) so we'll call it MyNonTemplatedClass:
class MyNonTemplatedClass
{
public:
template<typename T>
void DoSomething(T & t)
{
DoSomethingHelper(t, typename is_random_class<T>::type());
}
// ...
Notice how DoSomething is now templated, and it's actually calling a helper instead of implementing the logic itself?
Let's break down the line:
DoSomethingHelper(t, typename is_random_class<T>::type());
t is as-before; we're passing along the argument of type T&
typename is_random_class<T>::type()
is_random_class<T> is our concept, and since it derives from std::true_type or std::false_type it will have a ::type defined within the class (Google for "type traits")
::type() 'instantiates' the type specified by is_random_class<T>::type. I say it in quotation marks because we're really going to throw that away as we see later
typename is required because the compiler doesn't know that is_random_clas<T>::type actually names a type.
Now we're ready to look at the rest of MyNonTemplatedClass:
private:
//use tag dispatch. If the compiler is smart it won't actually try to instantiate the second param
template<typename T>
void DoSomethingHelper(T&t, std::true_type)
{
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
}
template<typename T>
void DoSomethingHelper(T&t, std::false_type)
{
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
Full Working Demo v2 Here
Notice that our helper functions are named the same, but overloaded on the second parameter's type. We don't give a name to the parameter because we don't need it, and hopefully the compiler will optimize it away while still calling the proper function.
Our concept forces DoSomethingHelper(T&t, std::true_type) only if T is of type SomeRandomClass, and calls the other for any other type.
The benefit of tag dispatch
The main benefit of tag dispatch here is that you don't need to specialize your entire class if you only mean to specialize a single function within that class.
The tag dispatching will happen at compile time, which you wouldn't get if you tried to perform branching on the concept solely within the DoSomething function.
C++17
In C++17, this problem becomes embarrassingly easy using variable templates (C++14) and if constexpr (C++17).
We use our type_trait to create a variable template that will give us a bool value of true if the provided type T is of type SomeRandomClass, and false otherwise:
template<class T>
constexpr bool is_random_class_v = is_random_class<T>::value;
Then, we use it in a if constexpr expression that only compiles the appropriate branch (and discards the other at compile-time, so the check is at compile-time, not run-time):
struct MyNonTemplatedClass
{
template<class T>
void DoSomething(T& t)
{
if constexpr(is_random_class_v<T>)
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
else
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
type-traits were a way to simulate this without needing a class specialization.
Note that is_random_class here is a stand-in for an arbitrary constraint. In general, if you're only checking for a single nontemplated type, prefer a normal overload because it's more efficient on the compiler.
Demo
C++20
In C++20, we can take this a step further and use a constraint instead of if constexpr by using a requires clause on our templated member function. The downside is that we again move back to two functions; one that matches the constraint, and another that doesn't:
struct MyNonTemplatedClass
{
template<class T> requires is_random_class_v<T>
void DoSomething(T& t)
{
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
}
template<class T> requires !is_random_class_v<T>
void DoSomething(T&)
{
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
Demo
Also in C++ 20, we could explicitly encode a concept and use abbreviated template syntax:
template<class T>
concept IsRandomClass = is_random_class_v<T>;
template<class T>
concept IsNotRandomClass = !is_random_class_v<T>;
// ...
template<IsRandomClass T>
void DoSomething(T& t)
{ /*...*/}
template<IsNotRandomClass T>
void DoSomething(T&)
{ /*...*/}
Demo
All you need to do is just template on what you want to keep generic. Taking what you started with:
template<typename T, typename S>
void MyTemplateClass< SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S> & t)
{
//something specialized happens here
}
EDIT:
Alternatively, if you only want to keep part of the SomeRandomClass generic, you could:
template<typename T>
void MyTemplateClass< SomeRandomClass<T,int> >::DoSomething(SomeRandomClass<T,int> & t)
{
//something specialized happens here
}
Edit: this is a correct answer to a different question.
Using the typename T twice confuses the issue a little, because they are compiled separately and are not connected in any way.
You can overload the method to take a templated parameter:
template <typename T>
class MyTemplateClass
{
void DoSomething(T& t) { }
template <typename U,typename V>
void DoSomething(SomeRandomClass<<U,V>& r) { }
};
This maps U and V in the new method to T' and S' in SomeRandomClass. In this setup, either U or V could be the same type as T, but they don't have to be. Depending on your compiler, you ought to be able to do
MyTemplateClass<string> mine;
SomeRandomClass<int,double> random;
// note: nevermind the non-const ref on the string literal here...
mine.DoSomething("hello world");
mine.DoSomething(random);
and the templated call will be selected as the matching overload without having to respecify the types explicitly.
Edit:
To do with with template specialization makes no difference to the overload of DoSomething. If you specialize the class as follows
template <>
class SomeRandomClass <int,double>
{
// something here...
};
then the overload above will eat up this specialized implementation gladly. Just be sure the interfaces of the specialized template and the default template match.
If what you're wanting is to specialize DoSomething to take a specific pair of types for SomeRandomClass then you've already lost generality...that's what specialization is.
If you want to use provide a template struct as a template argument (with intent to use it inside) without specializing it:
Here is an example, that appends a type to a tuple given a template sfinae struct as a template argument:
template<typename Tuple, typename T, template<typename> class /*SFINAEPredicate*/>
struct append_if;
template<typename T, template<typename> class SFINAEPredicate, typename ... Types>
struct append_if<std::tuple<Types...>, T, SFINAEPredicate>
{
using type = typename std::conditional<SFINAEPredicate<T>::value,
std::tuple<Types..., T>, std::tuple<Types...>>::type;
};
// usage
using tuple_with_int = append_if<std::tuple<>, int, std::is_fundamental>;
This can be used since C++11.