template specialization using enable_if in function definition - c++

if I have the following function
struct Struct { template<T> void Foo(); }
How can I use enable_if in the function definition without repeating the declaration above?
template<T> typename enable_if<is_class<T>,void>::type Struct::Foo() { ... } // error: Struct has no member `Foo<T>`
template<T> typename enable_if<!is_class<T>,void>::type Struct::Foo() { ... } // error: Struct has no member `Foo<T>`
enable_if<is_class<T>,void> is just an example but is there a way to not repeat the declaration with multiple enable_if definitions?
it seems I'm forced to do this
struct Struct
{
template<T> typename enable_if<is_class<T>,void>::type Foo();
template<T> typename enable_if<!is_class<T>,void>::type Foo();
}

The easiest way to not have to repeat yourself too much is to define them inline:
#include <type_traits>
struct Struct {
template<class T>
std::enable_if_t<std::is_class_v<T>> Foo() { /* ... */ }
// same as:
// std::enable_if_t<std::is_class_v<T>, void> Foo() { /* ... */ }
template<class T>
std::enable_if_t<!std::is_class_v<T>> Foo() { /* ... */ }
// same as:
// std::enable_if_t<!std::is_class_v<T>, void> Foo() { /* ... */ }
};
or with constexpr-if:
struct Struct {
template<class T>
void Foo() {
if constexpr (std::is_class_v<T>) {
/* ... */
} else {
/* ... */
}
}
};
which makes separating the declaration and definition slightly less verbose:
struct Struct {
template<class T>
void Foo();
};
template<class T>
void Struct::Foo() {
if constexpr (std::is_class_v<T>) {
} else {
}
}

Related

Need a way to check template type and if not in the range of allowed types then have compile time error in C++

I need to have a template class where each object adds itself to a vector and based on the template type parameter(allowed only: string, int, float) add to the corresponding container. I need a way to have compile time checks for the type and based on the check add to the corresponding container and if the type is not one of the allowed types compile time error should be emitted.
Example: code
vector<myClass<int>*> intVec;
vector<myClass<float>*> floatVec;
vector<myClass<string>*> stringVec;
template<typename T>
struct myClass
{
myClass()
{
/*pseudo code
if(T == int) {
intVec.push_back(this);
}
else if(T == float) {
floatVec.push_back(this);
}
else if(T == string){
stringVec.push_back(this);
}
else {
// error
}
*/
}
T value;
}
How can I achieve this ?
In C++17 and later, you can use if constexpr and std::is_same_v, eg:
#include <type_traits>
template<typename T>
struct myClass
{
myClass()
{
if constexpr (std::is_same_v<T, int>) {
m_intVec.push_back(this);
}
else if constexpr (std::is_same_v<T, float>) {
m_floatVec.push_back(this);
}
else if constexpr (std::is_same_v<T, std::string>){
m_stringVec.push_back(this);
}
else {
// error
}
}
T value;
};
In earlier versions, you can use either template specialization or SFINAE instead, eg:
// via specialization
template<typename T>
struct myClass
{
};
template<>
struct myClass<int>
{
myClass()
{
m_intVec.push_back(this);
}
int value;
};
template<>
struct myClass<float>
{
myClass()
{
m_floatVec.push_back(this);
}
float value;
};
template<>
struct myClass<std::string>
{
myClass()
{
m_stringVec.push_back(this);
}
std::string value;
};
// via SFINAE
#include <type_traits>
template<typename T>
struct myClass
{
template<typename U = T, std::enable_if<std::is_same<U, int>::value, int>::type = 0>
myClass()
{
m_intVec.push_back(this);
}
template<typename U = T, std::enable_if<std::is_same<U, float>::value, int>::type = 0>
myClass()
{
m_floatVec.push_back(this);
}
template<typename U = T, std::enable_if<std::is_same<U, std::string>::value, int>::type = 0>
myClass()
{
m_stringVec.push_back(this);
}
T value;
};
Use specialization and a helper function, e.g.
template<typename T>
struct myClass;
inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;
template<typename T>
void add(myClass<T>*);
template<>
void add(myClass<int>* p) {
intVec.push_back(p);
}
template<>
void add(myClass<float>* p) {
floatVec.push_back(p);
}
template<>
void add(myClass<std::string>* p) {
stringVec.push_back(p);
}
template<typename T>
struct myClass
{
myClass()
{
add(this);
}
T value;
};
in addition to existing answers, you can also do it with normal function overloading
template<typename T>
struct myClass;
inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;
/* optional
template<typename T>
constexpr bool always_false = false;
template<typename T>
void add(myClass<T>*) {
static_assert(always_false<T>,"unsupported T");
}
*/
void add(myClass<int>* p) {
intVec.push_back(p);
}
void add(myClass<float>* p) {
floatVec.push_back(p);
}
void add(myClass<std::string>* p) {
stringVec.push_back(p);
}
template<typename T>
struct myClass
{
myClass()
{
add(this);
}
T value;
};

C++ inherit template specializations

I have a template function that does something depending on the type passed as a template argument:
template<class T>
void foo() {
if constexpr (std::is_integral_v<T>) {
// ...
} else if constexpr ... {
// ...
}
}
The problem with this it that I don't really need to have a function instantiation for every possible integral type (to avoid template code bloat), so I would like to have only one instance that takes a int and if we pass the type char or short it calls the int version. I would like something like this:
template<class T>
void foo() {
if constexpr (std::is_same_v<T, int>) { // Only check for `int`, because "lower" types will be converted to int
// ...
} else if constexpr ... {
// ...
}
}
foo<short>(); // Call `foo<int>`
foo<char>(); // Call `foo<int>`
foo<int>(); // Call `foo<int>`
Maybe I can wrap the function inside a struct and have a struct<char> that extends the struct<int>?
Note that there is no "values" anywhere, I just have template parameters.
You can turn foo into a callable object, and use using alias to select different instantiations according to T, for example:
#include <type_traits>
template<class T>
struct foo {
void operator()() {
if constexpr (std::is_same_v<T, int>) {
// ...
} else if constexpr ... {
// ...
}
}
};
template<class T>
auto bar = std::conditional_t<std::is_integral_v<T>, foo<int>, foo<T>>();
int main() {
bar<short>(); // Call `foo<int>`
bar<char>(); // Call `foo<int>`
bar<int>(); // Call `foo<int>`
bar<const char*>(); // Call `foo<const char*>`
}

Multiple inheritance with templated template

I want to do multiple inheritance via template arguments and pass reference to this in each base class, so I can call top level object's method from each base class's method. I can do it with manual inheritance, but I want to be able to do this via templates arguments.
Godbolt link
Godbolt link with manual inheritance
#include <cstdio>
template <typename T>
struct Foo {
Foo(T &t)
: t_(t) {
}
void foo() {
t_.call("foo");
}
T &t_;
};
template <typename T>
struct Bar {
Bar(T &t)
: t_(t) {
}
void bar() {
t_.call("bar");
}
T &t_;
};
template <template<typename> typename... Methods>
struct Impl : public Methods<Impl>... {
Impl()
: Methods<Impl>(*this)... {
}
void call(const char *m) {
printf(m);
}
};
int main() {
auto t = Impl<Foo, Bar>();
t.foo();
t.bar();
}
I tried this approach, but it gives
type/value mismatch at argument 1 in template parameter list for 'template<class> class ... Methods'
Thanks to #Nicol Bolas, he advised to use static_cast and CRTP for this
#include <cstdio>
template <typename T>
struct Foo {
void foo() {
static_cast<T*>(this)->call("foo");
}
};
template <typename T>
struct Bar {
void bar() {
static_cast<T*>(this)->call("bar");
}
};
template <template<typename> typename... Methods>
struct Impl : public Methods<Impl<Methods...>>... {
Impl() {
}
void call(const char *m) {
printf(m);
}
};
int main() {
auto t = Impl<Foo, Bar>();
t.foo();
t.bar();
}

c++ template specialization for base class

I would like to have a special formatter for BASECLASS and all derived classes. I have the following classes:
struct BASECLASS { ... };
struct SPECIALFORMAT : BASECLASS { ... }
struct ANOTHERSPECIALFORMAT : BASECLASS { ... }
template <class T>
struct LISTFORMATTER {
list<T> l;
bool format() {
};
}
bool LISTFORMATTER<BASECLASS>::format() { ... }
LISTFORMATTER<BASECLASS> bcFt;
LISTFORMATTER<SPECIALFORMAT> spFt;
LISTFORMATTER<ANOTHERSPECIALFORMAT> aspFt;
bcFt.format(); // <-- ok
spFt.format(); // <-- Calling standard format(), not specialized
aspFt.format(); // <-- Calling standard format(), not specialized
How can I specialize a method for a base class and all inherited classes?
EDIT preferible not using boost. c++ (not c++11)
First, you need is_base_of. If you don't want to use Boost or C++11, then grab one here:
How does `is_base_of` work?
Then, you can do this:
template <bool B> struct bool_ {};
// ...
bool format() { do_format(bool_<is_base_of<BASECLASS, T>::value>()); }
bool do_format(bool_<false>) {
// not inheriting BASECLASS
}
bool do_format(bool_<true>) {
// inheriting BASECLASS
}
BTW, there is, AFAIK, no way of doing this non-intrusively, i.e. simply by adding a specialization.
Edit: Actually, you can probably do it without is_base_of:
// ...
bool format() { do_format((T*)0); }
bool do_format(void*) { /* not inheriting */ }
bool do_format(BASECLASS*) { /* inheriting */ }
This works because derived->base is a better conversion than class->void.
I think you could do this with enable_if and is_base_of (either from c++11 or boost).
Tag dispatching may help:
struct BASECLASS { };
struct SPECIALFORMAT : BASECLASS { };
struct ANOTHERSPECIALFORMAT : BASECLASS { };
template <typename T>
struct IsDerivedFromBASECLASS {
static const bool value = false; //std::is_base_of<BASECLASS, T>::value;
};
template <>
struct IsDerivedFromBASECLASS<BASECLASS> { static const bool value = true; };
template <>
struct IsDerivedFromBASECLASS<SPECIALFORMAT> { static const bool value = true; };
template <>
struct IsDerivedFromBASECLASS<ANOTHERSPECIALFORMAT> { static const bool value = true; };
template <class T>
struct LISTFORMATTER {
//list<T> l;
bool format();
};
template <typename T, bool IsABASECLASS>
struct helper_format {
bool operator() (LISTFORMATTER<T>&)
{
// default implementation
return true;
}
};
template <typename T>
struct helper_format<T, false>
{
bool operator() (LISTFORMATTER<T>&)
{
// specialization implementation
return false;
}
};
template<typename T>
bool LISTFORMATTER<T>::format() {
return helper_format<T, IsDerivedFromBASECLASS<T>::value>(*this);
}

Specialize template member inside template definition

It is possible to specialize some class member function outside the template definition:
template<class A>
struct B {
void f();
};
template<>
void B<int>::f() { ... }
template<>
void B<bool>::f() { ... }
and in this case I can even omit definition of function f for a general type A.
But how to put this specializations inside the class? Like this:
template<class A>
struct B {
void f();
void f<int>() { ... }
void f<bool>() { ... }
};
What syntax should I use in this case?
EDIT:
For now the solution with fewest lines of code is to add a fake template function f definition and explicitly call it from original function f:
template<class A>
struct B {
void f() { f<A>(); }
template<class B>
void f();
template<>
void f<int>() { ... }
template<>
void f<bool>() { ... }
};
You should put the specialization on the struct:
template<>
struct B<int> {
void f() { ... }
};
template<>
struct B<bool> {
void f() { ... }
};
There is no way to specialize member functions in the same class the templated version is defined in. Either you must explicitly specialize the member function outside of the class, or specialize an entire class with the member function in it.
You can make B::f a template function within your struct:
struct B {
template <typename T>
void f();
template<>
void f<int>() { ... }
template<>
void f<bool>() { ... }
};
Edit:
According to your comment this may help you, but I've not tested if it works:
template <typename A>
struct B {
template <typename T = A>
void f() { ... }
template<>
void f<int>() { ... }
template<>
void f<bool>() { ... }
};
#include<iostream>
using namespace std;
template<class A>
class B
{
public:
void f() {
cout << "any" << endl;
}
};
template<>
class B<int>
{
public:
void f() {
cout << "int" << endl;
}
};
int main()
{
B<double> b1;
b1.f();
B<int> b2;
b2.f();
return 0;
}
Output:
any
int
Anything else is not possible.