SFINAE并检测C++函数对象是否返回void

Tom*_*rly 7 c++ sfinae

我已经阅读了有关这方面的各种权威,包括Dewhurst,但却没有设法通过这个看似简单的问题.

我想要做的是调用 C++ 函数对象(基本上,你可以调用的任何东西,纯函数或带()的类,并返回它的值,如果它不是void,否则返回"true".

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
Run Code Online (Sandbox Code Playgroud)

UPDATE

感谢您的想法和想法!

基于此,我实际上决定提出我所有的模板水平 - 所以我有:

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; }
Run Code Online (Sandbox Code Playgroud)

和泛型类来承载各种对象和方法.感谢Mr.Ree的代码推动了我朝这个方向发展!

Ale*_* C. 6

要在编译时检测void返回值,标准技巧是重载operator,.逗号运算符很酷的是它可以采用void参数,在这种情况下它默认为内置operator,.在代码中:

template <typename> tag {};

template <typename T>
tag<T> operator,(T, tag<void>);
Run Code Online (Sandbox Code Playgroud)

现在,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();    
};
Run Code Online (Sandbox Code Playgroud)


Mr.*_*Ree 1

实现print(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) );
}
Run Code Online (Sandbox Code Playgroud)


好吧,我开始看到更多的问题。

虽然我们可以这样做:

#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()));
}
Run Code Online (Sandbox Code Playgroud)

您仍然需要将&CLASS::operator()作为参数输入。


最终,虽然我们可以确定对象的operator()方法是否返回void,但我们通常不能根据返回类型进行重载。

我们可以通过模板专门化来解决重载限制。但随后我们就陷入了这种丑陋的境地,我们仍然需要指定类型......尤其是返回类型!手动或通过传入合适的参数,我们可以从中提取必要的类型。

顺便说一句:#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 );
}
Run Code Online (Sandbox Code Playgroud)