Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to specify multiple types for a function parameter?

It is possible in C++ define a function argument to be of more than one type?

#include <iostream>
using namespace std;

class A {
public:
   void  PrintA() { cout << "A" << endl;}
};

class B {
public:
   void  PrintB() { cout << "B" << endl;}
};

class C: public A, public B {
public:
   C(){;}
};

class D: public A, public B {
public:
   D(){;}
};

///
void __printall__(A*a, B*b){
   a->PrintA();
   b->PrintB();
}
#define printall(a) __printall__(a,a)
///

int main(int argc, char *argv[]){
   C c;
   D d;
   printall(&c);
   printall(&d);
}

I want to change the code between the comments with something not using macros. I would not force the pointers cast because I want to preserve the type safety. I would not even introduce another class between C/D and A/B because actually my class hierarchy is a bit more complex of that shown in the code, and it is not desirable to rebase all the class that derive from A or B

like image 342
user1534420 Avatar asked Jul 18 '12 10:07

user1534420


3 Answers

Like @Torsten advised, a possible solution is to use a function template so that you can pass any type of arguments. However, a simple template will work with any type that provides the appropriate members (in this case printA and printB), so the following function template

template <typename T>
void printAll(T const & t)
{
    t.printA();
    t.printB();
}

will work with the following type

struct Foo
{
    void printA() const { std::cout << "FooA\n"; }
    void printB() const { std::cout << "FooB\n"; }
}

printAll(Foo());

even though Foo does not derive from A or B. This might be desirable, but if you really want to enforce the fact that the argument must be a A and a B, you can use a static assertion in your function to check that:

#include <type_traits>

template <typename T>
void printAll(T const & t)
{
    static_assert(std::is_base_of<A, T>::value && std::is_base_of<B, T>::value,
                  "T must be derived from A and B");

    t.printA();
    t.printB();
} 

An alternate solution would be to use std::enable_if to define the function template only when the template parameter is indeed a derived class of A and B:

template<
    typename T ,
    typename = typename std::enable_if<
        std::is_base_of<A, T>::value &&
        std::is_base_of<B, T>::value
    >::type
>
void printAll(T const & t)
{
    t.printA();
    t.printB();
}

N.B: static_assert, enable_if and is_base_of are C++11 features. If you are working with C++03, you can find the equivalent in various Boost libraries.

like image 75
Luc Touraille Avatar answered Oct 10 '22 13:10

Luc Touraille


a template version will just pick the type, that is passed to the function:

template < class T >
void printall( T* t )
{
   t->printA();
   t->printB();
}
like image 25
Torsten Robitzki Avatar answered Oct 10 '22 14:10

Torsten Robitzki


Torsten is right. Instead, if are looking something like "late binding" at run-time you could use a function pointer. You can find a clear example here.

like image 32
alcor Avatar answered Oct 10 '22 15:10

alcor