I have a template class C<T> which I intend to instantiate with T as some other classes A and B. C<T> has a method foo whose signature I would like to depend on whether T was instantiated as A or B. For example, consider the following code:
#include <iostream>
#include <string>
class A {
public:
void message() {
std::cout << "message with no args" << std::endl;
}
};
class B {
public:
void message(int x) {
std::cout << "message with " << x << std::endl;
}
};
template<typename T>
class C {
private:
T internal;
public:
C(T& x) {
internal = x;
}
void call() {
internal.message();
}
void call(int x) {
internal.message(x);
}
};
int main(int argc, char* argv[]) {
A a;
B b;
C<A> ca(a);
C<B> cb(b);
ca.call();
cb.call(42);
// ca.call(42); ERROR HERE
return 0;
}
This runs correctly. ca.call(42) would raise a compilation error because there is no method A::message(int). However, if I for some reason introduce a method A::message(int) in A, the code may allow calling ca.call(42), which I would like to prevent.
I know that SFINAE techniques would allow to declare a method C::call(T::call_type x) where T::call_type would be a typedef for each intended instantiation of T. However, this only allows me to change the type of the argument of C::call. I would like instead to make the signature (in particular, the number of parameters) of C::call on T. I would thus prevent ca.call(42) from being a valid call even if there is a method A::message(int) in A.
Is there any way to do this?
I don't know all the ins and outs of SFINAE, but what do you think of this?
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, A>::value>>
void call() {
internal.message();
}
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value>>
void call(int x) {
internal.message(x);
}
You can use == false too
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value == false>>
You can do this with template specializations:
// Main template used by every other type T.
template<typename T>
class C; // or some other implementation.
// This gets used for T = A.
template<>
class C<A> {
private:
A internal;
public:
C(A& x) {
internal = x;
}
void call() {
internal.message();
}
};
// This gets used for T = B.
template<>
class C<B> {
private:
B internal;
public:
C(B& x) {
internal = x;
}
void call(int x) {
internal.message(x);
}
};
If you don't like that you need to duplicate some common code, then you can have a base class with all those common things and inherit from it in each specialization.
Related
I've got an abstract class that uses variable template.
template <class T>
class Abstract
{
public:
virtual void print(T t) = 0;
};
There can be any derivatives of the class like so:
class A : public Abstract<std::string>
{
public:
void print(std::string str)
{
std::cout << str << std::endl;
}
};
class B : public Abstract<int>
{
public:
void print(int number)
{
std::cout << std::to_string(number) << std::endl;
}
};
Now I want a function to return one of these derivatives so I can execute the print method. And here is my Problem:
template (class T); // error here
Abstract<T> &f(int n) // what should the return type look like?
{
if (n == 0)
{
A a{};
return a;
}
else
{
B b{};
return b;
}
}
int main()
{
A a{f(0)};
a.print("foo");
B b{f(1)};
b.print(42);
return 0;
}
So how is it be possible to return a class with unknown parameter type and call its methods?
I already tried returning derived classes without templates which works fine. As soon as templates are added code wont compile. I also tried void* and reinterpret_cast. Problem here is that I have manually to decide to what type to cast to.
So how can I return an arbitrary superclass of an abstract generic class and call its generic methods?
I think inheritance is the wrong approach here. Instead I would use specialization instead:
template<typename T>
struct Foo;
template<>
struct Foo<std::string>
{
void print(std::string const& s)
{
std::cout << s << '\n';
}
};
template<>
struct Foo<int>
{
void print(int value)
{
std::cout << value << '\n';
}
};
Then you don't need a selector to pick the object to create, just the correct type:
int main()
{
Foo<std::string> f1;
f1.print("hello");
Foo<int> f2;
f2.print(123);
}
If you really need a factor function, then it could be created like this:
template<typename T>
Foo<T> create()
{
return Foo<T>();
}
And use like
int main()
{
auto f1 = create<std::string>();
f1.print("hello");
auto f2 = create<int>();
f2.print(123);
}
First of all, the code is restricted to C++11, so I cannot make use of if constexpr
Following is my sample code snippet:
class A{
public:
int a;
int b;
}
class B{
public:
int key;
int val;
}
class C{
public:
int n1;
int n2;
}
class D{
public:
int n1;
int n2;
}
class E{
public:
int n1;
int n2;
}
template<typename T>
void func1(T data) {
if (T == type(A)) { // Just pseudo template-check code
std::cout<<data.a<<data.b; //<------1
} else if (T == type (B)) { // Just pseudo template-check code
std::cout<<data.key<<data.val; //<------2
} else {
std::cout<<data.n1<<data.n2; //<------3
}
int main() {
A a;
B b;
C c;
D d;
E e;
func1(a);
func1(b);
func1(c);
func1(d);
func1(e);
return 0;
}
Currently, I get a compile-time error at,
1: B,D,E,F has no member a & b
&
2: A,D,E,F has no member key & val
&
3. A, B has no member n1 & n2
I tried using is_same() & also this, but I get same compile time error every time.
I cannot make use of C++14/C++17
How could I make use of specialized template functions?
Edited the code to highlight the need of a template.
You can use a function overload and avoid the function template altogether.
void func1(A a)
{
// Type dependent code.
}
void func1(B a)
{
// Type dependent code.
}
A function template makes sense only if there is common code for all the types for which the function call is made. If you have some code that is common to all types and some code that are type dependent, then you can use:
void func1(A a)
{
// Type dependent code.
}
void func1(B a)
{
// Type dependent code.
}
template <typename T>
void func2(T t)
{
// Type independent code.
}
template <typename T>
void func(T obj)
{
func1(obj); // Call function that uses type dependent code.
func2(obj); // Call function that uses type independent code.
}
You must write specializations of the function for the two types your want to use it with.
#include<iostream>
class A{
public:
int a;
int b;
};
class B{
public:
int key;
int val;
};
template<typename T>
void func1(T);
template<>
void func1<A>(A arg) {
std::cout<<"A"<<std::endl;
std::cout<<arg.a<<arg.b;
}
template<>
void func1<B>(B arg) {
std::cout<<"B"<<std::endl;
std::cout<<arg.key<<arg.val;
}
int main(){
A a;
func1(a);
B b;
func1(b);
}
Simple overload does the job.
template <typename T>
void func1(T data)
{
std::cout << data.n1 << data.n2;
}
void func1(A data)
{
std::cout << data.a << data.b;
}
void func1(B data)
{
std::cout << data.key << data.val;
}
https://godbolt.org/z/r7Ee6E
Tweaked a bit: https://godbolt.org/z/xxPWaE
Here is a problem I stumbled accross when refactoring some code and I was wondering if there is a better way to do it:
#include <iostream>
template<typename T>
class Foo
{
public:
Foo()
{
init(x);
}
T x;
};
void init(int& i)
{
i = 42;
}
int main()
{
Foo<int> foo;
std::cout << foo.x << std::endl;
return 0;
}
Unfortunately, this doesn't compile - neither with GCC or with Clang. The function init called in Foo's constructor is not declared. In this toy example, this could be solved by moving the function itself ahead of the template. However, in a more complex context, this may not work. Originally, I intended to use overloads of init to allow some setup for the classes used in the template.
I assumee that in this context init is a non-dependend name - even though the argument of the function call depends on the template parameter (which was odd for me at first). Is there a way to trick it to also consider function definitions defined after the template itself?
I know that I can use template specialization (which was in the original code in the first place, but I wanted to replace it with simpler overloads):
template<typename>
struct Initializer;
template<>
struct Initializer<int>
{
static void init(int& i)
{
i = 42;
}
}
Is there a way to make this work with function overloads as well? I know, boost::serialization also relies on function overloads for custom types, but I did not really find where and how they implemented that.
You can solve the ordering problem by calling through a template function object - in the same way that boost::hash finds the ADL-function hash_value(x).
This works because the expansion of the template is deferred until the point of first use:
#include <iostream>
namespace A {
struct XX {
friend void init(XX&);
};
}
namespace B {
struct YY {
friend void init(YY&);
};
}
/// default case - call init on T found by ADL
template<class T>
struct call_init
{
void operator()(T& o) const {
init(o);
}
};
template<typename T>
class Foo
{
public:
Foo()
{
auto initialiser = call_init<decltype(this->x)>();
initialiser(this->x);
}
T x;
};
void init(int& x) {
x = 2;
}
// special case, initialise an int
template<> struct call_init<int>
{
void operator()(int& x) const {
init(x);
}
};
int main()
{
Foo<int> foo;
Foo<A::XX> foox;
Foo<B::YY> fooy;
std::cout << foo.x << std::endl;
return 0;
}
Your problem is also that intis not a class and no ADL is done for it, replacing int by custom class works:
template<typename T>
class Foo
{
public:
Foo() { init(x); }
T x;
};
struct C
{
int i = 0;
};
void init(C& c) { c.i = 42; }
Demo.
To allow to works for primitive type as int, you have to introduce some custom type:
template <typename> struct tag {};
template<typename T>
class Foo
{
public:
Foo() { init(x, tag<T>{}); }
T x;
};
void init(int& i, tag<int>) { i = 42; }
Demo
I have the following code (very simplified for the sake of clarity):
class Base
{
virtual int DoStuff(int arg) = 0;
};
template <typename T>
class Derived : public Base
{
int DoStuff(int arg) override
{
// do some stuff
return 0;
}
};
This works great. Now I want to implement a special (vectorized) implementation of DoStuff. And I need the implementation to be specific based on the type T that Derived has, something like this:
class Base
{
virtual int DoStuff(int arg) = 0;
virtual int DoStuffVectorized(int arg) = 0;
};
template <typename T>
class Derived : public Base
{
int DoStuff(int arg) override
{
// do some stuff
return 0;
}
int DoStuffVectorized<char>(int arg) override
{
// do some stuff for T == char
return 0;
}
int DoStuffVectorized<int>(int arg) override
{
// do some stuff for T == int
return 0;
}
};
However i'm unable to make this work.
EDIT:
I get the following error message: error C2143: syntax error: missing ';' before '<' on the line int DoStuffVectorized<char>(int arg) override.
When i change it to:
template<char> int DoStuffVectorized(int arg) override i get: error C2898: ...': member function templates cannot be virtual
Any advice on how to achieve something like this? The reason i need it is that i have a std::vector that stores data of various types (by using Derived<>). This way i can use the same simple code regardless of the type being stored and i want this to be true even when using the special vectorized implementation of DoStuff that is sadly type specific.
You have to specialize template member functions outside of the class:
#include <iostream>
class Base
{
public:
virtual int DoStuffVectorized(int arg) = 0;
};
template <typename T>
class Derived : public Base
{
public:
int DoStuffVectorized(int arg) override;
};
template <>
int Derived<char>::DoStuffVectorized(int arg)
{
std::cout << "T == char\n";
return 0;
}
template <>
int Derived<int>::DoStuffVectorized(int arg)
{
std::cout << "T == int\n";
return 0;
}
int main(){
Derived<char> c;
Derived<int> i;
Base* b[] = { &c, &i };
for(auto* x : b)
x->DoStuffVectorized(0);
// undefined reference to `Derived<double>::DoStuffVectorized(int)'
// Derived<double> d;
}
If you want to capture unintended instantiations at compile time:
#include <type_traits>
// A std::false_type (useful in a static_assert)
template <typename T>
struct static_false : std::false_type
{};
template <typename T>
int Derived<T>::DoStuffVectorized(int arg)
{
static_assert(static_false<T>::value, "Neither 'char' or 'int'");
return 0;
}
DoStuffVectorized<char> is not correct syntax, DoStuffVectorized isn't template itself.
See template specialization:
template <typename T>
class Derived : public Base
{
int DoStuff(int arg) override
{
// do some stuff
return 0;
}
int DoStuffVectorized(int arg) override
{
// do some stuff (primary template)
return 0;
}
};
template <>
int Derived<int>::DoStuffVectorized(int) {
// do some stuff for T == char
return 0;
}
template <>
int Derived<char>::DoStuffVectorized(int) {
// do some stuff for T == char
return 0;
}
I am using C++ and templates - but I need to allow using a member function for a specific type and prevent other types from using this function.
For example: I want this class to have print() for all types but have foo() for just type int. How can I do that ?
#include<iostream>
template <class type>
class example
{
private:
type data;
public:
void print(); // for all types
void foo(); // only for 'type' == int?
};
Factor the common, generic functionality into a base class. Specializations can inherit that.
namespace detail {
template <class type>
class example_base
{
private:
type data ;
public:
void print();
};
} // end namespace detail
template <class type>
struct example
: detail::example_base<type> {
using detail::example_base<type>::example_base; // inherit any constructors
};
template <> // specialize the class
struct example< int >
: detail::example_base<int> {
using detail::example_base<int>::example_base; // inherit any constructors
void other_function(); // extend the basic functionality
};
You can specify the template for some types. See this example :
template <typename T>
class MyClass
{
private:
double my_function() { return 1.0; }
};
template <>
class MyClass<double>
{
public:
double my_function() { return 2.0; }
};
int main()
{
MyClass<double> a;
MyClass<int> b;
double x = a.my_function(); // this works
// double y = b.my_function(); // not allowed
return 0;
}
Tested with GCC 4.7.2.
You could use std::enable_if and do something like this to obtain exactly what you want:
#include <iostream>
#include <type_traits>
template<class T>
class example
{
private:
T data;
public:
void print()
{
std::cout << " hello " << std::endl;
}
template<class U = T // make the function templated on U, but with default type T
, typename std::enable_if<std::is_integral<U>::value>::type* = nullptr // enable only for when U (T) is an integral type
>
void foo()
{
std::cout << " I will only compile when T is an integral type " << std::endl;
}
};
int main()
{
example<int> integer_example;
integer_example.print();
integer_example.foo(); // <--- compiles fine as int is integral
example<double> double_example;
double_example.print();
//double_example.foo(); // <--- will not compile
return 0;
}
In the std::enable_if you can also put std::is_same<U,int>::value instead of std::is_integral<U>::value to only allow the function only to be used for int and not other integral types.