Template member function specialization in a template class - c++

I have a template class and a member function print() to print the data.
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
Question: Why does this member function specialization get an error? What is the correct way to define a print function for a vector?
Solution 1: I have tested the following definition.
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
This one worked, but I have to copy the other member functions into this specialized class.
EDIT:
Solution 2: To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
This solution works well. Are there some better or more conventional solutions?

Why does this member function specialization get error?
When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.
Are there some better solutions?
Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
(See Live Online)
This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.
If you don not have access to C++17, other option is to SFINAE the members(Since c++11).
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
(See Live Online)
What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?
You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
And the print function could be in c++17:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
(See Live Online)

You need to implement a template class that uses a vector as template parameter. This worked for me.
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};

You could always use named tag dispatching to check if type provided by template user is vector.
A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself.
Below is code I used named tag dispatching as solution to your problem:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler
Based on answer: Check at compile-time is a template type a vector

You can dispatch printing to another member function (static or not). For example:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};

Related

std::is_member_function_pointer not working for overloaded functions? [duplicate]

This question already has answers here:
Checking for existence of an (overloaded) member function
(3 answers)
Closed 2 days ago.
I've written a trait that detects whether a class has a certain public member function via std::is_member_function_pointer:
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
template <typename T>
struct HasMethod<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 0
}
However it seems that when the function is overloaded, then std::is_member_function_pointer is not able to detect it, as shown with Test2.
Is there a way to make it work regardless of whether the method is overloaded?
To detect existence of class members usual approach is to use std::void_t template. You can do it like this:
#include <iostream>
#include <type_traits>
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
// Use method(0) to detect void method(int) overload
// You don't even need std::void_t in this case, since method returns void anyway, but for any other return type you will need it
template <typename T>
struct HasMethod<T, std::void_t<decltype(std::declval<T>().method())>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 1
}
Ok. After some thinking I found solution. We can use fact that declval(&T::method) will fail, if there is more than one overload, to detect if we have at least one overload, by adding another one. Here is solution. It is quite verbose, but I was unable to reduce it. At least it works.
#include <iostream>
#include <type_traits>
class Test
{
};
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
class Test3
{
public:
using method = int;
};
class Test4
{
public:
int method;
};
template<typename T, typename = void>
struct HasSingleOverload : std::false_type {};
template<typename T>
struct HasSingleOverload<T, std::void_t<decltype(&T::method)>> : std::true_type {};
template<typename T, typename = void>
struct IsMemberFunction : std::false_type {};
template<typename T>
struct IsMemberFunction<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>> : std::true_type {};
template<typename T, typename = void>
struct IsType : std::false_type {};
template<typename T>
struct IsType<T, std::void_t<typename T::method>> : std::true_type {};
struct HasOverload {
void method();
};
template<typename T>
struct CheckOverload : T, HasOverload {
};
template<typename T>
using HasConflict = std::bool_constant<!HasSingleOverload<CheckOverload<T>>::value>;
template<typename T>
using HasAnyOverload = std::conditional_t<HasSingleOverload<T>::value || IsType<T>::value, IsMemberFunction<T>, HasConflict<T>>;
int main()
{
std::cout << HasAnyOverload<Test>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test1>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test2>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test3>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test4>::value << std::endl; // prints 0
}

bind non static templated member function in derived class

#include <functional>
#include <iostream>
class Plain {
public:
template <typename Type>
void member_function(const Type& s) {
std::cout << "Recived: " << s << std::endl;
}
};
template <typename Type>
class Templated : private Plain {
public:
};
int main() {
Plain b;
b.member_function<int>(10); // done!
Templated<int> d;
// d.member_function(); /* how to achive this */
return 0;
}
I am trying to call the member function in class Plain by two method:
createing non-templated class and padding type while calling function
Plain p;
p.member_function<int>();
passing type while creating class and calling without template param
Templated<int> t;
t.member_function(); // achive this
I tried doing binding the function in derived class like
struct Plain{
template<typename T>
static void member_function(const T& s){std::cout << s << std::endl;}
}
template<typename T>
struct Templated : private Plain {
std::function<void(const T&)> print = Templated::Plain::member_function;
}
and after that I was able to do
Templated t<std::string>;
t.print();
When you use private inheritance the methods in Plain are inaccessible to outside code, and you need to have something inside of Templated make the call to the method in Plain; you can do so, or alternatively you could use public inheritance and be able to hit it directly.
class Plain {
public:
template <typename T>
void print(const T & s) {
std::cout << "Received: " << s << std::endl;
}
};
template <typename T>
class Templated : private Plain {
public:
void print(const T & s) {
Plain::print<T>(s);
}
};
template <typename T>
class Alternative : public Plain {};
int main() {
Templated<int> t;
t.print(3); // This could work
Alternative<int> a;
a.print(4); // As could this
return 0;
}
I found a workaround
#include <functional>
#include <iostream>
using namespace std::placeholders;
struct Test {
template <typename Type>
void foo(const Type&) {
std::cout << "I am just a foo..." << std::endl;
return;
}
};
template <typename T>
struct Foo {
private:
Test* obj;
public:
Foo() : obj(new Test) {}
std::function<void(const int&)> foo = std::bind(&Test::foo<T>, obj, _1);
~Foo() { delete obj; }
};
int main() {
Foo<int> me;
me.foo(10);
Test t;
t.foo<int>(89);
std::cout << std::endl;
return 0;
}

How to specialise template method with type that itself is a template where only the return type relies on the template type?

I want to specialise a single template method in a non-template class to use an std::vector however only the return type of the method uses the template.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
template<>
int Foo::Get()
{
std::cout << "int" << std::endl;
return 12;
}
template<typename T>
std::vector<T> Foo::Get()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
}
This compiles with an error indicating that the std::vector attempted specialisation does not match any prototype of Foo, which is completely understandable.
In case it matters, use of C++14 is fine and dandy.
You can only partially specialize classes (structs) (cppreference) - so the way to overcome your problems is to add helper struct to allow this partial specialization of std::vector<T> - e.g. this way:
class Foo
{
private: // might be also protected or public, depending on your design
template<typename T>
struct GetImpl
{
T operator()()
{
std::cout << "generic" << std::endl;
return T();
}
};
public:
template<typename T>
auto Get()
{
return GetImpl<T>{}();
}
};
For int - you can fully specialize this function:
template<>
int Foo::GetImpl<int>::operator()()
{
std::cout << "int" << std::endl;
return 12;
}
For std::vector<T> you have to specialize entire struct:
template<typename T>
struct Foo::GetImpl<std::vector<T>>
{
std::vector<T> operator()()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
Partial specialisation of template functions (including member functions) is not allowed. One option is to overload instead using SFINAE. For example,
/// auxiliary for is_std_vetor<> below
struct convertible_from_std::vector
{
template<typename T>
convertible_from_std::vector(std::vector<T> const&);
};
template<typename V>
using is_std_vector
= std::is_convertible<V,convertible_from_std_vector>;
class Foo
{
public:
template<typename T, std::enable_if_t< is_std::vector<T>::value,T>
Get()
{
std::cout << "vector" << std::endl;
return T();
}
template<typename T, std::enable_if_t<!is_std::vector<T>::value,T>
Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
Note that the helper class is_std_vector may be useful in other contexts as well, so it worth having somewhere. Note further that you can make this helper class more versatile by asking for any std::vector or specific std::vector<specific_type, specific_allocator>. For example,
namespace traits {
struct Anytype {};
namespace details {
/// a class that is convertible form C<T,T>
/// if either T==AnyType, any type is possible
template<template<typename,typename> C, typename T1=Anytype,
typename T2=Anytype>
struct convCtTT
{
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T1=Anytype>
struct convCtTT<C,T1,AnyType>
{
template<typename T2>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T2=Anytype>
struct convCtTT<C,AnyType,T2>
{
template<typename T1>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C>
struct convCtTT<C,AnyType,AnyType>
{
template<typename T1, typename T2>
convCtTT(C<T1,T2> const&);
};
}
template<typename Vector, typename ValueType=AnyType,
typename Allocator=AnyType>
using is_std_vector
= std::is_convertible<Vector,details::convCtTT<std::vector,ValueType,
Allocator>;
}
You can't partially specialze template in c++. You need to overload your function and pass the type in parameters.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
return this->getTemplate(static_cast<T*>(0)); //
}
private:
template<class T> T getTemplate(T* t)
{
std::cout << "generic" << std::endl;
return T();
}
template<class T> std::vector<T> getTemplate(std::vector<T>* t)
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
template <> int Foo::getTemplate(int* t)
{
std::cout << "int" << std::endl;
return 12;
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
auto v = foo.Get<std::vector<int>>();
}
Edit : fixed a typo in the code

Template specialization using another class that implicit-converts to it

I don't know if this is possible, but I would like to understand better how this works.
Can a class implict convertsion operation be used to match a template parameter?
This is what I want to do.
#include <iostream>
template<typename T>
struct Value {
};
template<>
struct Value<int> {
static void printValue(int v) {
std::cout << v << std::endl;
}
};
struct Class1 {
int value;
};
/*
template<>
struct Value<Class1*> {
static void printValue(Class1* v) {
std::cout << v->value << std::endl;
}
};
*/
template<typename X>
struct ClassContainer {
ClassContainer(X *c) : _c(c) {}
operator X*() { return _c; }
X *_c;
};
template<typename X>
struct Value<ClassContainer<X>> {
static void printValue(ClassContainer<X> v) {
std::cout << static_cast<X*>(v)->value << std::endl;
}
};
template<typename X>
void doPrintValue(X v)
{
Value<X>::printValue(v);
}
int main(int argc, char *argv[])
{
doPrintValue(10);
Class1 *c = new Class1{ 20 };
//doPrintValue(c); // error C2039: 'printValue': is not a member of 'Value<X>'
ClassContainer<Class1> cc(c);
doPrintValue(cc);
std::cout << "PRESS ANY KEY TO CONTINUE";
std::cin.ignore();
}
ClassContainer has an implict conversion to X*. Is it possible to match ClassContainer passing only X*?
If you want the template class for pointers to behave like the template class for something else, just inherit:
template<typename T>
struct Value<T*> : Value<ClassContainer<T>> {};
It will inherit the public printValue function, which accepts a parameter that can be constructed from T*, and everything will be implicitly converted as expected.
See it all live here.

Gaining access to class members from template specialization

So, I basically want to "add" extra stuff to the class, depending on what template arguments its being used with and then aliasing it for nicer interface. Something like this:
template<typename T, std::size_t N>
struct basic_foo
{
T data[N];
};
//what you see as comments is what i tried
template<> //template<typename T, std::size_t N>
struct basic_foo<double, 3> //: public basic_foo<T, N>
{
void foo_fun()
{
std::cout << "I'm a foo function!";
for (auto i : data) std::cout << i << " ";
}
};
template<> //template<typename T, std::size_t N>
struct basic_foo<int, 2> //: public basic_foo<T, N>
{
void bar_fun()
{
std::cout << "I'm a bar function!";
for (auto i : data) std::cout << i << " ";
}
};
using foo = basic_foo<double, 3>;
using bar = basic_foo<int, 2>;
int main()
{
foo a = { 12.0, 2.4, 3.0 };
bar b = { 1, 2 };
}
The problem is that I can't access data in the specializations.
Is there a way to do this? Or should I rethink my structural decisions?
Your code, as it is now does not compile, because you don't have data member in your specializations. You have tried to inherit it from the primary template, and you almost got it.
Make a basic_foo_base class (or struct) template, where you store data and functions that needn't be specialized:
template <typename T, std::size_t N>
struct basic_foo_base
{
T data[N];
};
That was just adding _base to the name of the existing definition. Now, define basic_foo again:
template <typename T, std::size_t N>
struct basic_foo : basic_foo_base<T, N>
{
};
And this is how a specialization will look:
template <>
// instantiate the base with the same template arguments
struct basic_foo<double, 3> : public basic_foo_base<double, 3>
{
};
I hope I got everything right.
Edit: No, I didn't.
Because of inheritance, basic_foo won't be an aggregate anymore. We'll need to add some code, so the initialization:
foo a = { 12.0, 2.4, 3.0 };
is valid again. That is, defining an implicit constructor taking std::initializer_list:
basic_foo_base(std::initializer_list<T> const& il)
{
std::copy(il.begin(), il.end(), std::begin(data));
}
And adding
using basic_foo_base::basic_foo_base;
// or, for the primary template:
using basic_foo_base<T, N>::basic_foo_base;
in all basic_foos to include that constructor to the overload resolution.
Working code
Alternatively to common base class which contain data, you may fully specialize:
template<>
struct basic_foo<double, 3>
{
double data[3]; // Should be here.
void foo_fun()
{
std::cout << "I'm a foo function!";
for (auto i : data) std::cout << i << " ";
}
};