c++11 metaprogramming: check for method existence [duplicate] - c++

This question already has answers here:
Templated check for the existence of a class member function?
(33 answers)
Closed 6 years ago.
1) I have two classes class A and class B which both have a method called foo but with different argument lists.
class A {
public:
void foo(int a);
};
class B {
public:
void foo(int a, int b);
};
2) Further, I have a class C with template argument T that also has a foo method as following
template <typename T>
class C {
public:
void foo();
private:
T t_;
int a_;
int b_;
};
3) I would like use both class A and class B as template argument for class C.
Saying that I would like to have a method C::foo to be implemented like following:
template <typename T>
void C<T>::foo()
{
if (compile time check: T has foo(int a, int b))
t_.foo(a_, b_);
else
t_.foo(a_);
}
How can I express the if statement above in C++11?

Use SFINAE (with function template overloading).
template <typename T>
class C {
private:
T t_;
int a_;
int b_;
public:
template <typename X = T>
auto foo() -> decltype (std::declval<X>().foo(a_)) {
t_.foo(a_);
}
template <typename X = T>
auto foo() -> decltype (std::declval<X>().foo(a_, b_)) {
t_.foo(a_, b_);
}
};
LIVE

If you know which type contains void foo(int a, int b); which in this case you do, you can use template specialization, something like
template <typename T>
class C
{
private:
T t_;
int a_;
int b_;
public:
// called for general use
void foo()
{
t_.foo(a_);
}
};
// specialized version for T = B
template<>
void C<B>::foo()
{
t_.foo(a_, b_);
}

Related

C++20 concepts how to define existence of a function with arguments?

In C++20, we can now use concepts instead of SFINAE to figure out whether a function exists in a template typename:
template<typename T> concept fooable = requires (T a) {
a.foo();
};
class Foo {
public:
// If commented out, will fail compilation.
void foo() {}
void bar() {}
};
template <typename T> requires fooable<T>
void foo_it(T t) {
t.bar();
}
int main()
{
foo_it(Foo());
}
How do we do this with functions that have non-empty arguments?
You might have extra parameters in requires:
template<typename T> concept fooable = requires (T a, int i) {
a.foo(i);
};
Demo
The best option seems to be declval:
template<typename T> concept fooable = requires (T a) {
a.foo(std::declval<int>());
};
class Foo {
public:
void foo(int x) {}
void bar() {}
};
template <typename T> requires fooable<T>
void foo_it(T t) {
t.bar();
}
int main()
{
foo_it(Foo());
}

Multiple function definition for templated function due to inheritance

I cannot compile my program with VS2015 due to a function using a templated parameter and inheritance.
The error is this one.
I'm trying to achieve the following :
class A
{
//do something
};
class B : public A
{
//do something
};
template <typename T>
class Foo {
template <typename T>
friend void function(Foo<T> & sm) {
//do something
}
};
void main()
{
Foo<A> test;
Foo<B> test2;
};
I do understand the meaning of the error, but I do not understand why it actually happens.
I suppose function is created with two different signatures :
void function(Foo<A> & sm); and
void function(Foo<B> & sm);
How is that a multi-definition?
EDIT - The full error message :
Error C2995 'void function(Foo<T> &)': function template has already been defined
EDIT² - From scratch
Both Clang and MS have the same complaint. Remove the second template specifier and it will compile.
class A{};
class B : public A{};
template <typename T>
class Foo {
// template <typename T>
friend void function(Foo<T> & sm) {
}
};
int main()
{
Foo<A> test;
Foo<B> test2;
};
T is already specified for the class Foo so its friend function is covered. You would us a second template if there were a difference at the function, like:
class A{};
class B : public A{};
template <typename T>
class Foo {
template <typename U>
friend void function(Foo<T> & sm, U another) {
}
};
int main()
{
Foo<A> test;
Foo<B> test2;
};

How does one specialize a template for all non-array types?

Let's say I have a template my_type. I want it to have general functionality, to have a few extra functions when T is not an array and to have others when T is an array.
Let's say I have the following template:
template <typename T>
class my_class<T> {
public:
int f1(); // This function is available for all T
int f2(); // This function is available when T is not an array
int f3(); // This function is available when T is an array
}
So if I try:
my_class<int> c1; my_class<int[3]> c2;
c1.f1(); c2.f1(); // both fine
c1.f2(); c2.f3(); // both fine
c1.f3(); c2.f2(); // both should give a compile error
I am aware std::unique_ptr does this internally. So how does it do it?
Another way, using enable_if. Note also the use of a base class to capture all common behaviour.
#include <type_traits>
template<class T>
struct my_base
{
int f1();
};
template<class T, typename Enable = void>
class my_class;
template<class T>
class my_class<T, std::enable_if_t<std::is_array<T>::value>>
: public my_base<T>
{
public:
int f3(); // This function is available when T is an array
};
template <typename T>
class my_class<T, std::enable_if_t<not std::is_array<T>::value>>
: public my_base<T>
{
public:
int f2(); // This function is available when T is not an array
};
int main()
{
auto a = my_class<int[]>();
a.f1();
// a.f2();
a.f3();
auto na = my_class<int>();
na.f1();
na.f2();
// na.f3();
}
I have figured it out myself. The following code will do the exact thing I have asked for.
template<typename T>
class my_class {
public:
int f1() { return 1; }
int f2() { return 2; }
};
template<typename T>
class my_class<T[]> {
public:
int f1() { return 1; }
int f3() { return 3; }
};
Note that the implementation of the common function (f1) had to be copied. Now is there a way to use a single implementation? (note that it is NOT as simple as a return 1; like in the example code and thus I can't separate functionality into a non-template function)

Template arguments in constructor of non-template class

I want to have a constructor of a non-template class which is templated by a type. Can anyone help here?
class A
{
public:
static int GetId(){ return 5;}
};
class B
{
public:
B(int id){ _id = id;}
template<typename T>
B() {_id = T::GetId();}
template<typename T>
static B* newB() {return new B(T::GetId());}
private:
int _id;
};
void doSome()
{
B* p1 = B::newB<A>(); //works
B* p2 = new B<A>(); //doesn't compile -- ">>B<< is no template"
}
All template parameters of a constructor template must be deducible (or have default arguments), because there is no syntax for explicitly passing template arguments to a constructor (as you've learned).
There are several possible ways around this:
Provide a constructor-like function template. You're already doing this with newB, there's just no need to force dynamic allocation:
template <class T>
B create() { return B(T::GetId()); }
Provide a tag type and parameterise the consturctor by that:
template <class T>
struct Tag {};
class B
{
public:
template <class T>
B(Tag<T>) : _id(T::GetId()) {}
};
//usage:
B b(Tag<A>());
You cannot explicitly specify the constructor template parameter. It must be deductible.
One solution is to use a helper parameter:
template <class T>
struct Type_holder { using Type = T; };
class B {
public:
B(int id) : id{id} {}
template<typename T>
B(Type_holder<T>) : id{T::GetId()} {}
private:
int id;
};
auto foo()
{
B b{Type_holder<A>{}};
}
Also, please use constructor initialization lists. And careful with those dynamic allocations. Don't use it if it's not needed. And when it's needed use smart pointers.

Class data member type based another data member?

Let's say I have a base class foo and two derived classes A and B. I then have another class bar, which has a data members x, y, z, which can be either A,or, but the types depends on other data members x_type, y_type, and z_type, and those values are not available at compile time. I though about using template data member and defining the type in constructor, where I get the values for the types but apparently that is not possible at least in C++11. So how to proceed?
class foo{
public:
foo(double);
int x_type;
virtual double do_something(double, int) = 0;
};
class A: public foo {
public:
A(double, double);
double do_something(double, int);
private:
double z1;
double z2;
};
class B: public foo {
public:
B(double);
double do_something(double, int);
private:
double w;
};
class bar {
public:
bar();
double do_something2(int);
private:
int x_type;
int y_type;
int x_type;
x; // these are either A or B...
y;
z;
};
And in constructor I would have something like
if(x_type == 1){
x = A(arg1, arg2);
} else {
x = B(arg3);
}
In my real application there can be much higher number of derived classes and data members with unknown types. I was wondering if it is possible to make bar a template class with multiple template parameters, but I am not sure if that is possible either as the parameter type depends on another parameter?
You need to use polymorphism and take advantage of the common base class Foo:
private:
int x_type;
int y_type;
int x_type;
std::unique_ptr<Foo> x; // these are either A or B...
std::unique_ptr<Foo> y;
std::unique_ptr<Foo> z;
};
Then in your constructor you can create x y z from the correct type:
if(x_type == 1){
x.reset(new A(arg1, arg2));
} else {
x.reset(new B(arg3));
}
It is a good practice to move the code that creates the correct Foo instance in a so called "factory" class or function in order to hide the decision making logic and construction specifics (which might be quite complex at times).
If all type that can be used for x, y and z are all derived from a common base class, the base-pointer solution, with std::unique_ptr (+1 for Lyubomir Stankov), is (IMHO) a good solution.
But you asked "if it is possible to make bar a template class with multiple template parameters".
Yes: it's possible. Not really elegant (ever IMHO) but possible.
I propose the following solution for fun but I think that, in a more general case (note that, in my example, A and B are unrelated classes, not more derived from foo), can be useful (I hope so)
#include <tuple>
#include <string>
#include <utility>
class A
{
private:
double d;
std::string s;
public:
A (double d0, std::string s0) : d { d0 }, s { s0 } { }
};
class B
{
private:
long l;
public:
B (long l0) : l { l0 } { }
};
template <typename Tx, typename Ty, typename Tz>
class bar
{
private:
template <typename ... Ts>
using tpl = std::tuple<Ts...>;
template <std::size_t ... Is>
using is = std::index_sequence<Is...> const;
template <std::size_t N>
using mis = std::make_index_sequence<N>;
Tx x;
Ty y;
Tz z;
template <typename ... Tsx, std::size_t ... Isx,
typename ... Tsy, std::size_t ... Isy,
typename ... Tsz, std::size_t ... Isz>
bar (tpl<Tsx...> const & tx0, is<Isx...> const &,
tpl<Tsy...> const & ty0, is<Isy...> const &,
tpl<Tsz...> const & tz0, is<Isz...> const &)
: x { std::get<Isx>(tx0) ... },
y { std::get<Isy>(ty0) ... },
z { std::get<Isz>(tz0) ... }
{ }
public:
template <typename ... Tsx, typename ... Tsy, typename ... Tsz>
bar (tpl<Tsx...> const & tx0,
tpl<Tsy...> const & ty0,
tpl<Tsz...> const & tz0)
: bar(tx0, mis<sizeof...(Tsx)> {},
ty0, mis<sizeof...(Tsy)> {},
tz0, mis<sizeof...(Tsz)> {})
{ }
};
int main()
{
bar<A, B, A> aba{ std::make_tuple(2.3, "str1"),
std::make_tuple(4),
std::make_tuple(5.4, "str2") };
bar<B, A, B> bab{ std::make_tuple(3),
std::make_tuple(3.2, "str3"),
std::make_tuple(5) };
}
Unfortunately this example use std::make_index_sequence and std::index_sequence that are C++14 features.
If you want implement foo in C++11, you can implement the following structs struct indexSeq and struct indexSeqHelper, to substitute std::index_sequence and std::make_index_sequence
template <std::size_t ...>
struct indexSeq
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSeqHelper
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; };
template <std::size_t ... Next >
struct indexSeqHelper<0U, Next ... >
{ using type = indexSeq<Next ... >; };
and define is and mis as follows
template <std::size_t ... Is>
using is = indexSeq<Is...>;
template <std::size_t N>
using mis = typename indexSeqHelper<N>::type;
The static type of all variables must be known at compile time, so it cannot change based on the value of a run time object. The way to make this work is to make x, y, and z all have the type std::uniqe_ptr<foo> and then dynamically allocate an A or B object at run time:
class bar {
public:
bar(some_type something) {
if (something == some_value) {
b.x = new A(3.14, 12.34);
} else {
b.x = new B(456.78);
}
}
private:
int x_type;
std::unique_ptr<foo> x;
//...
};
int main() {
bar b(whatever());
}
In this case you should also declare foo::~foo() to be virtual so that you ensure the derived objects get destroyed correctly.
It would also be a generally Good Idea™ to eliminate x_type and friends entirely and write code that doesn't care about the actual type of x once it's created.
I was wondering if it is possible to make bar a template class with multiple template parameters, but I am not sure if that is possible either as the parameter type depends on another parameter?
I don't know if this helps, but I'll let it here just in case.
You see, different specializations of a template can inherit from different classes. So that you can have:
// fwd decl
template <int kind> class bar;
template <> class bar<1> : public A {
public:
bar(double x, double y) : A(x,y) { }
};
template <> class bar<2> : public B {
public:
bar(double a) : B(a) { }
};
At later stage, when you come with a class C : public foo, you just assign another kind to a new bar template specializations and there you have it: using bar as a unifying name (warning... but not a unifying type - not other than the common foo ancestor. bar<1> and bar<2> will be two different types)
So, Ok, if you don't want inheritance, you can have it by different has-a in specific bar template specializations.
Like
template <int kind> class bar;
template <> class bar<1> {
A val;
public:
bar(double x, double y) : val(x,y) { }
void doSomething2(...) {
// use the val of type A
}
};
template <> class bar<2> {
B val;
double y_;
public:
bar(double x, double y) : val(x), y_(y) { }
void doSomething2(...) {
// use the val of type B and a separate y_
}
};
I though about using template data member and defining the type in
constructor, where I get the values for the types but apparently that
is not possible at least in C++11
C++11 comes with the standard schema of dealing with templated construction depending on some parameter by using make_* template function creating appropriate type of object. See e.g. make_tuple function:
auto t = std::make_tuple(1, "abc", 1.0);
// decltype(t) == std::tuple<int, char const*, double>
This can be implemented by creating template class and template construction function:
template <class T>
struct foo {
T t;
foo(T t): t(t) { }
};
template <class T>
foo<T> make_foo(T t) { return foo<T>(t); }