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
}
Related
Let's say I have a reader class over a file:
class Reader {
public:
template <class T>
T Read();
};
Its only function is the Read function that reads any arithmetic type (static_assert(std::is_arithmetic_v<T>)) from a file. Now I want to create a specialization of that function, which reads a vector from the file. How would I go about doing that with templates? Something like the following doesn't work:
template <class T>
std::vector<T> Read<std::vector<T>>();
error: function template partial specialization is not allowed
std::vector<U> Read<std::vector<U>>();
^ ~~~~~~~~~~~~~~~~
You can't partially specialize functions. You can overload them though, but the way of doing it is not obvious, since your function doesn't take any parameters.
First, you need a way to check if a type is a std::vector<??>:
template <typename T> struct IsVector : std::false_type {};
template <typename ...P> struct IsVector<std::vector<P...>> : std::true_type {};
Then you can plug it into requires:
template <typename T>
T Read()
{
// Generic overload
}
template <typename T> requires IsVector<T>::value
T Read()
{
// Vector overload
}
Alternatively, you could have a single function, with if constexpr (IsVector<T>::value) inside.
A way to implement what you want is to delegate the logic of your member function to a couple of private member functions:
#include <cstdio>
#include <vector>
class BinaryReader {
public:
template <class T>
T Read() {
T t{};
this->ReadImpl(t);
return t;
}
private:
template <class T>
void ReadImpl(T& t) {
static_assert(std::is_arithmetic_v<T>);
std::puts("T");
t = T{}; // implement your logic here
}
template <class T>
void ReadImpl(std::vector<T>& t) {
std::puts("std::vector<T>");
t = std::vector<T>{}; // implement your logic here
}
};
int main() {
BinaryReader br;
br.Read<int>();
br.Read<std::vector<int>>();
}
This doesn't require you to introduce new type traits to check if your type is a std::vector<>. However, it requires your return types to be default constructible.
Output:
T
std::vector<T>
In C++17, we have std::void_t, which makes SFINAE look a lot nicer:
template <typename T>
std::void_t<decltype(T::prop)> foo() { /* stuff */ }
The template function will exist only if T::prop exists.
If T::prop exists, the template function foo() would be equivalent to this:
template <typename T>
void foo() { /* stuff */ }
Otherwise, the code is equivalent to not declaring foo() at all.
Is there any generalization of std::void_t for other types in the standard library, such as the following:
template<typename T, typename...>
using generic_t = T;
so that the code below would be valid?
template <typename T>
std::generic_t<int, decltype(T::prop)> foo() { /* stuff */ }
which would be equivalent to
template <typename T>
int foo() { /* stuff */ }
if T::prop exists?
Why do you need such a generalization? void_t is a little special in that it helps you easily write type traits, because you can have a primary with some type defaulted to void and a specialization which uses void_t. For instance:
template <class T, class = void>
struct has_prop : std::false_type { };
template <class T>
struct has_prop<T, std::void_t<decltype(T::prop)>> : std::true_type { };
It's not that there's anything special about void, you just need some agreed upon type between the primary and the specialization.
void_t doesn't make much sense if you're just using it directly in SFINAE though. You could just stick the expression somewhere else:
template <typename T, class = decltype(T::prop)>
void foo() { /* stuff */ }
at which point the return type is totally separate from the condition you're checking anyway, so if you want int:
template <typename T, class = decltype(T::prop)>
int foo() { /* stuff */ }
It probably does not exist. It is not linked in the documentation and therefore I doubt its existence. But you can build such type on your own:
template <class type, class... sfinae_expressions>
using generic_t = type;
Say, I have some template which specialized for several types, TypeMathcer, which has type member.
#include <memory>
#include <vector>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
Now, I want to create another template and specialize it for types I get from TypeMatcher. If I do it straightforward, like this
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
{
};
I get compiler error: template parameters not deducible in partial specialization.
Same error if use using syntax
template <typename T>
using type_matcher_t = typename TypeMatcher<T>::type;
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<type_matcher_t<T> >
{
};
I read answer to question partial specialization for iterator type of a specified container type that is very similar to my question, but still not sure if existing of one counter-example makes all question senseless. Also now we have brand-new c++14 and c++17 standards which could change situation. So what if I ensure the specializations is unique and exists, will than any possibility to make parameters deducible?
This is impossible, on principle, and no fancy C++9999 can change that.
What you're asking the compiler to do:
There's a use such as MyNeedfulTemplate<int> in the code. The compiler needs a definition of MyNeedfulTemplate<U> for U = int. You've tried to provide a partial specialisation of the form
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
To see whether this specialisation applies or not, the compiler would have to inspect TypeMatcher<T> for all possible Ts and find if any one of them has a nested typedef type that aliases int. This cannot happen, as the set of "all possible Ts" is infinite. OK, TypeMatcher<int> doesn't have such a type, and neither does TypeMatcher<int*>, nor TypeMatcher<int**>, nor TypeMatcher<int***>. But what if TypeMatcher<int****> does? Better keep trying...
Also remember that partial and complete specialisation exists, meaning that TypeMatcher itself could be specialised.
In short, there is no way to link an int to a TypeMatcher<X>::type if all you have is the int and not the X.
You should be able to achieve something similar by re-structuring (inverting) TypeMatcher a bit:
template <class T>
struct TypeMatcher2
{
static constexpr specialised = false;
};
template <class T>
struct TypeMatcher2<std::shared_ptr<T>>
{
static constexpr specialised = true;
using OldType = T*;
};
template <class T>
struct TypeMatcher2<std::vector<T>>
{
static constexpr specialised = true;
using OldType = T&;
}
template <class T, bool spec = TypeMatcher2<T>::specialised>
struct MyNeedfullTemplate
{
// generic version
};
template <class T>
struct MyNeedfullTemplate<T, true>
{
using OriginalT = typename TypeMatcher2<T>::OldType;
// specialised version
};
I think what you're trying to do is this:
#include <iostream>
#include <memory>
#include <vector>
#include <utility>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
template <typename T, typename = void>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<TypeMatcher<T>, std::enable_if_t<std::is_same<typename TypeMatcher<T>::type, std::vector<std::remove_reference_t<T>>>::value>>
{
static void report() { std::cout << "hello" << std::endl; }
};
int main()
{
using matcher_type = TypeMatcher<int&>;
using full_type = MyNeedfullTemplate<matcher_type>;
full_type::report();
return 0;
}
Do I understand the question correctly?
Considering class templates, it is possible to provide template specializations for certain types of groups using type traits and dummy enabler template parameters. I've already asked that earlier.
Now, I need the same thing for function templates: I.e., I have a template function and want a specialization for a group of types, for example, all types that are a subtype of a class X. I can express this with type traits like this:
std::enable_if<std::is_base_of<X, T>::value>::type
I thought about doing it this way:
template <typename T, typename ENABLE = void>
void foo(){
//Do something
}
template <typename T>
void foo<T,std::enable_if<std::is_base_of<A, T>::value>::type>(){
//Do something different
}
However, this does not work since partial specialization is not allowed for function templates. So how to do it then? Maybe a default parameter with the type trait as type? But how does the code look like then?
Overloads:
void foo_impl(T, std::false_type);
void foo_impl(T, std::true_type);
foo(T t) { foo_impl(t, std::is_base_of<A, T>()); }
The closest to what you're asking is enable_if on the return type:
template<typename T> typename std::enable_if<std::is_same<T, int>::value>::type foo();
template<typename T> typename std::enable_if<std::is_same<T, char>::value>::type foo();
However, dispatching to a helper function or class is likely to be more readable and efficient.
Helper function:
template<typename T> void foo_helper(std::true_type);
template<typename T> void foo_helper(std::false_type);
template<typename T> void foo() { foo_helper(std::is_same<T, int>()); }
Helper class:
template<typename T, bool = std::is_same<T, int>::value> struct foo_helper {};
template<typename T> struct foo_helper<T, true> { static void foo(); };
template<typename T> struct foo_helper<T, false> { static void foo(); };
template<typename T> void foo() { foo_helper<T>::foo(); }
Do the actual implementation (partial specializations etc..) in class templates and write a small wrapper template function that does nothing but call a static function in your class templates.
Tried a few things and finally came up with the correct syntax myself - sorry for asking. I didn't know that enable_if has a second parameter. By using this parameter and a default value, it is possible.
Here is the answer
template<typename T>
void foo(typename std::enable_if<std::is_base_of<A, T>::value,int>::type ENABLER = 0){
std::cout << "T is a subclass of A!";
}
template<typename T>
void foo(typename std::enable_if<!std::is_base_of<A, T>::value,int>::type ENABLER = 0){
std::cout << "T is NOT a subclass of A";
}
I asked this question earlier where a solution was presented. The solution is great as far as the question is concerned, but now I am confused on how I would define the methods outside of the class i.e. I would like to define the methods in an .inl file. What would be the syntax in this case?
Just to be clear, for a template class, the method definition will be:
template <typename T>
struct Foo
{
Foo();
};
// C-tor definition
template <typename T>
Foo<T>::Foo()
{
}
How would I define methods for the template class with enable_if as one of the parameters?
template <typename Policy, enable_if< is_base<BasePolicy, Policy>::value >::type >
struct Foo
{
Foo();
};
// C-tor definition -- ???
From the looks of it, you want to do something along the lines of this:
template <typename Policy,
typename = typename std::enable_if<std::is_base_of<BasePolicy, Policy>::value>::type >
struct Foo;
template <typename Policy>
struct Foo<Policy> {
Foo();
};
template <typename Policy>
Foo<Policy>::Foo() {
}
This sneakily takes advantage of the default argument in a few places: don't get confused, there is an implicit void sitting in several locations.
Here's how SFINAE can actually work with partial specialization:
template<typename T, typename Sfinae = void>
struct Foo {
/* catch-all primary template */
/* or e.g. leave undefined if you don't need it */
};
template<typename T>
struct Foo<T, typename std::enable_if<std::is_base_of<BasePolicy, T>::value>::type> {
/* matches types derived from BasePolicy */
Foo();
};
The definition for that constructor can then be awkwardly introduced with:
template<typename T>
Foo<T, typename std::enable_if<std::is_base_of<BasePolicy, T>::value>::type>::Foo()
{
/* Phew, we're there */
}
If your compiler supports template aliases (it's a C++11 feature) that then you can cut a lot of the verbosity:
template<typename T>
using EnableIfPolicy = typename std::enable_if<std::is_base_of<BasePolicy, T>::value>::type;
// Somewhat nicer:
template<typename T>
struct Foo<T, EnableIfPolicy<T>> {
Foo();
};
template<typename T>
Foo<T, EnableIfPolicy<T>>::Foo() {}
Note: your original answer referred to utilies from Boost, like boost::enable_if_c and boost::is_base_of. If you're using that instead of std::enable_if and std::is_base_of (which are from C++11), then usage looks like
typename boost::enable_if<boost::is_case_of<BasePolicy, T> >::type
which has the advantage of getting rid of one ::value.