Sfinae и обнаружение, если объект функции C ++ возвращает void
Вопрос
Я прочитал различные власти по этому поводу, включите Dewhurst И все же никуда не удалось добраться с этим, казалось бы, простым вопросом.
Что я хочу сделать вызов C ++ функциональный объект, (в основном, все, что вы можете позвонить, чистая функция или класс с ()), и вернуть его значение, если это не недействительно, или «истинно» в противном случае.
using std:
struct Foo {
void operator()() { cout << "Foo/"l; }
};
struct Bar {
bool operator()() { cout << "Bar/"; return true; }
};
Foo foo;
Bar bar;
bool baz() { cout << "baz/"; return true; }
void bang() { cout << "bang/"; }
const char* print(bool b) { cout << b ? "true/" : "false/"; }
template <typename Functor> bool magicCallFunction(Functor f) {
return true; // Lots of template magic occurs here
// that results in the functor being called.
}
int main(int argc, char** argv) {
print(magicCallFunction(foo));
print(magicCallFunction(bar));
print(magicCallFunction(baz));
print(magicCallFunction(bang));
printf("\n");
}
// Results: Foo/true/Bar/true/baz/true/bang/true
ОБНОВИТЬ
Спасибо за мысли и идеи!
Основываясь на этом, я на самом деле решил поднять весь свой шаблон на один уровень - поэтому вместо этого у меня есть:
bool eval(bool (*f)()) { return (*f)(); }
bool eval(void (*f)()) { (*f)(); return true; }
template <typename Type>
bool eval(Type* obj, bool (Type::*method)()) { return (obj->*method)(); }
template <typename Type>
bool eval(Type* obj, void (Type::*method)()) { (obj->*method)(); return true; }
и общие классы, чтобы нести различные объекты и методы вокруг. Спасибо мистеру за код, который подтолкнул меня в этом направлении!
Решение
Разве не было бы проще внедрить перегруженную версию NO-OP Печать (void)?
Ах, хорошо. Шаблоны функций и перегрузка будут легко обрабатывать это во время выполнения.
Это становится несколько липким, если вы хотели справиться с этим во время компиляции, для использования с макросами #IF или статическим аспектом времени.
Но так как вы хотите только первое, могу я предложить что -то подобное в качестве отправной точки:
(Протестировано под (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 () в как аргумент.
В конечном счете, хотя мы можем определить, есть ли объект оператор () Метод возвращает пустоту, мы обычно не можем перегружать на основе типов возврата.
Мы можем обойти это ограничение перегрузки с помощью специализации шаблона. Но тогда мы попадаем в эту уродливость, в которой нам все еще нужно указать типы ... особенно тип возврата! Либо вручную, либо путем прохождения подходящего аргумента, из которого мы можем извлечь необходимые типы.
Кстати: #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 );
}
Другие советы
Чтобы обнаружить void возвращаемого значения во время компиляции, стандартный трюк заключается в перегрузке operator,
. Анкет Крутая вещь с оператором запяты operator,
. Анкет В коде:
template <typename> tag {};
template <typename T>
tag<T> operator,(T, tag<void>);
В настоящее время, expr, tag<void>()
имеет тип tag<typeof(expr)>
даже если expr
имеет тип void. Затем вы можете поймать это обычными уловками:
char (&test(tag<void>))[1];
template <typename T> char (&test(tag<T>))[2];
template <typename F>
struct nullary_functor_traits
{
static const bool returns_void = sizeof(test((factory()(), tag<void>()))) == 1;
private:
static F factory();
};
Возможно, вы можете использовать тот факт, что void и не имеет смысла как тип, но void* делает.
С C ++ 0x вы можете сделать это легко, используя decltype
.
Если вы можете использовать Boost
, вероятно, следующий код. Я предполагаю, что все функции/функторы являются нультарными, как в вашем вопросе. Однако, чтобы использовать это, result_type
должен быть определен во всех функторах (функциональный класс).
#include <boost/utility/result_of.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>
using namespace boost; // Sorry, for brevity
template< class F >
// typename result_of< F() >::type
typename disable_if<
is_void< typename result_of< F() >::type >
, typename result_of< F() >::type
>::type
f( F const& x )
{
return x();
}
template< class F >
typename enable_if<
is_void< typename result_of< F() >::type >, bool
>::type
f( F const& x )
{
x();
return true;
}
template< class T >
T f( T x() )
{
return x();
}
bool f( void x() )
{
x();
return true;
}
static void void_f() {}
static int int_f() { return 1; }
struct V {
typedef void result_type;
result_type operator()() const {}
};
struct A {
typedef int result_type;
result_type operator()() const { return 1; }
};
int main()
{
A a;
V v;
f( void_f );
f( int_f );
f( a );
f( v );
}
Надеюсь это поможет
Попробуйте специализироваться на void return Type:
template<class F>
class traits;
template<class F, class T>
class traits<T (F)()>;
template<class F>
class traits<void (F)()>;
Я думаю ...