Разве не было бы проще реализовать перегруженную безоперационную версию print (void) ?
А-а-а.Шаблоны функций и перегрузка с легкостью справятся с этим во время выполнения.
Будет несколько сложнее, если вы захотите обработать это во время компиляции, для использования с макросами #if или static-compile-time-asserts.
Но так как вы хотите только первое, могу ли я предложить что-то вроде этого в качестве отправной точки:
(протестировано в (GCC) 3.4.4 и 4.0.1. - Я знаю, мне нужнообновить!)
#include <iostream>
using namespace std;
struct Foo {
void operator()() {}
};
struct Bar {
bool operator()() { return false; }
};
Foo foo;
Bar bar;
bool baz() { return false; }
void bang() {}
struct IsVoid
{
typedef char YES[1];
typedef char NO[2];
/* Testing functions for void return value. */
template <typename T>
static IsVoid::NO & testFunction( T (*f)() );
static IsVoid::YES & testFunction( void (*f)() );
static IsVoid::NO & testFunction( ... );
/* Testing Objects for "void operator()()" void return value. */
template <typename C, void (C::*)()>
struct hasOperatorMethodStruct { };
template <typename C>
static YES & testMethod( hasOperatorMethodStruct<C, &C::operator()> * );
template <typename C>
static NO & testMethod( ... );
/* Function object method to call to perform test. */
template <typename T>
bool operator() (T & t)
{
return ( ( sizeof(IsVoid::testFunction(t)) == sizeof(IsVoid::YES) )
|| ( sizeof(IsVoid::testMethod<T>(0)) == sizeof(IsVoid::YES) ) );
}
};
#define BOUT(X) cout << # X " = " << boolToString(X) << endl;
const char * boolToString( int theBool )
{
switch ( theBool )
{
case true: return "true";
case false: return "false";
default: return "unknownvalue";
}
}
int main()
{
IsVoid i;
BOUT( IsVoid()(foo) );
BOUT( IsVoid()(bar) );
BOUT( IsVoid()(baz) );
BOUT( IsVoid()(bang) );
cout << endl;
BOUT( i(foo) );
BOUT( i(bar) );
BOUT( i(baz) );
BOUT( i(bang) );
}
Хорошо, я начинаю видеть больше проблемы.
Хотя мы можем сделать что-то вроде этого:
#include <iostream>
using namespace std;
struct FooA {
void operator()() {}
};
struct FooB {
bool operator()() { return false; }
};
struct FooC {
int operator()() { return 17; }
};
struct FooD {
double operator()() { return 3.14159; }
};
FooA fooA;
FooB fooB;
FooC fooC;
FooD fooD;
void barA() {}
bool barB() { return false; }
int barC() { return 17; }
double barD() { return 3.14159; }
namespace N
{
/* Functions */
template <typename R>
R run( R (*f)() ) { return (*f)(); }
bool run( void (*f)() ) { (*f)(); return true; }
/* Methods */
template <typename T, typename R>
R run( T & t, R (T::*f)() ) { return (t .* f) (); }
template <typename T>
bool run( T & t, void (T::*f)() ) { (t .* f) (); return true; }
};
#define SHOW(X) cout << # X " = " << (X) << endl;
#define BOUT(X) cout << # X " = " << boolToString(X) << endl;
const char * boolToString( int theBool )
{
switch ( theBool )
{
case true: return "true";
case false: return "false";
default: return "unknownvalue";
}
}
int main()
{
SHOW( N::run( barA ) );
BOUT( N::run( barA ) );
SHOW( N::run( barB ) );
BOUT( N::run( barB ) );
SHOW( N::run( barC ) );
SHOW( N::run( barD ) );
cout << endl;
SHOW( N::run(fooA,&FooA::operator()));
BOUT( N::run(fooA,&FooA::operator()));
SHOW( N::run(fooB,&FooB::operator()));
BOUT( N::run(fooB,&FooB::operator()));
SHOW( N::run(fooC,&FooC::operator()));
SHOW( N::run(fooD,&FooD::operator()));
}
У вас все еще есть эта неприятная потребность кормить & CLASS :: operator () в качестве аргумента.
В конечном итогев то время как мы можем определить, возвращает ли метод объекта operator () пустоту, мы обычно не можем перегрузить, основываясь на возвращаемых типах.
Мы можем обойти это ограничение перегрузки с помощью специализации шаблона.Но затем мы попадаем в это уродство, где нам все еще нужно указывать типы ... Особенно тип возврата!Либо вручную, либо путем передачи подходящего аргумента, из которого мы можем извлечь необходимые типы.
Кстати: #define макросы тоже не помогут.Инструменты типа?: Требуют одинакового типа для обоих?и: часть.
Так что это лучшее, что я могу сделать ...
Конечно ...
Если вам не нужнотип возврата ...
Если вы просто передаете результат другой функции ...
Вы можете сделать что-то вроде этого:
#include <iostream>
using namespace std;
struct FooA {
void operator()() {}
};
struct FooB {
bool operator()() { return false; }
};
struct FooC {
int operator()() { return 17; }
};
struct FooD {
double operator()() { return 3.14159; }
};
FooA fooA;
FooB fooB;
FooC fooC;
FooD fooD;
void barA() {}
bool barB() { return false; }
int barC() { return 17; }
double barD() { return 3.14159; }
#define SHOW(X) cout << # X " = " << (X) << endl;
namespace N
{
template <typename T, typename R>
R run( T & t, R (T::*f)() ) { return (t .* f) (); }
template <typename T>
bool run( T & t, void (T::*f)() ) { (t .* f) (); return true; }
template <typename T>
void R( T & t )
{
SHOW( N::run( t, &T::operator() ) );
}
template <typename T>
void R( T (*f)() )
{
SHOW( (*f)() );
}
void R( void (*f)() )
{
(*f)();
SHOW( true );
}
};
int main()
{
N::R( barA );
N::R( barB );
N::R( barC );
N::R( barD );
N::R( fooA );
N::R( fooB );
N::R( fooC );
N::R( fooD );
}