用作模板函数输入的函数的 void 返回值被视为参数

发布于 2024-09-17 06:48:15 字数 2433 浏览 8 评论 0原文

假设您有一些带有一些方法的目标类:

class Subject
{
public:
  void voidReturn() { std::cout<<__FUNCTION__<<std::endl; }
  int  intReturn()  { std::cout<<__FUNCTION__<<std::endl; return 137; }
};

和一个 Value 类(在概念上与 Boost.Any 类似):

struct Value
{
  Value() {}
  Value( Value const & orig ) {}
  template< typename T > Value( T const & val ) {}
};

我想使用 Subject 类中的方法生成一个 Value 对象:

Subject subject;
Value intval( subject.intReturn() );
Value voidVal( subject.voidReturn() );  // compilation error

我在 VC+ 中收到以下错误+2008:

error C2664: 'Value::Value(const Value &)' : cannot convert parameter 1 from 'void' to 'const Value &'
Expressions of type void cannot be converted to other types

和 gcc 4.4.3:

/c/sandbox/dev/play/voidreturn/vr.cpp:67: error: invalid use of void expression

当您想在模板类中使用它时,上下文是这样的:

template< typename Host, typename Signature > class Method;

// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return Value( (m_Host->*m_Method)() ); }
private:
  Host       * m_Host;
  MethodType   m_Method;
};

在返回某些内容(即 intReturn)的方法上使用此 Method 类看起来像:

Method< Subject, int () > intMeth( &subject, &Subject::intReturn );
Value intValue = intMeth();

但是,使用 voidReturn 执行此操作方法:

Method< Subject, void () > voidMeth( &subject, &Subject::voidReturn );
Value voidValue = voidMeth();

产生与上面类似的错误。

一种解决方案是进一步部分专门化 void 返回类型的 Method:

template< typename Host >
class Method< Host, void () >
{
public:
  typedef void Return;
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return (m_Host->*m_Method)(), Value(); }
private:
  Host       * m_Host;
  MethodType   m_Method;
};

除了感觉丑陋之外,我还想专门化 X 个签名参数的 Method 类,这已经涉及大量代码重复(希望 Boost.Preprocessor 可以在这里提供帮助),然后添加 void 返回类型的专门化只会使重复工作加倍。

是否有办法避免对 void 返回类型进行第二次专门化?

Say you have some target class with some methods on it:

class Subject
{
public:
  void voidReturn() { std::cout<<__FUNCTION__<<std::endl; }
  int  intReturn()  { std::cout<<__FUNCTION__<<std::endl; return 137; }
};

And a Value class (similar in concept to Boost.Any):

struct Value
{
  Value() {}
  Value( Value const & orig ) {}
  template< typename T > Value( T const & val ) {}
};

And I want to produce a Value object using a method from the Subject class:

Subject subject;
Value intval( subject.intReturn() );
Value voidVal( subject.voidReturn() );  // compilation error

I get the following errors in VC++2008:

error C2664: 'Value::Value(const Value &)' : cannot convert parameter 1 from 'void' to 'const Value &'
Expressions of type void cannot be converted to other types

and gcc 4.4.3:

/c/sandbox/dev/play/voidreturn/vr.cpp:67: error: invalid use of void expression

The context for this is when you want to use it inside a templated class:

template< typename Host, typename Signature > class Method;

// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return Value( (m_Host->*m_Method)() ); }
private:
  Host       * m_Host;
  MethodType   m_Method;
};

Using this Method class on the method which returns something (namely intReturn) would look like:

Method< Subject, int () > intMeth( &subject, &Subject::intReturn );
Value intValue = intMeth();

However, doing this with the voidReturn method:

Method< Subject, void () > voidMeth( &subject, &Subject::voidReturn );
Value voidValue = voidMeth();

yields similar errors as above.

One solution is to further partially specialize Method for void return types:

template< typename Host >
class Method< Host, void () >
{
public:
  typedef void Return;
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return (m_Host->*m_Method)(), Value(); }
private:
  Host       * m_Host;
  MethodType   m_Method;
};

Besides it just feeling ugly, I'm also wanting to specialize the Method class for X numbers of signature parameters, which already involves a lot of code duplication (hopefuly Boost.Preprocessor can help here), and then adding a specialization for void return types just doubles that duplication effort.

Is there anyway to avoid this second specialization for void return types?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吾家有女初长成 2024-09-24 06:48:15

您可以使用 Return 并专门化 operator() 处理。无需复制整个模板。

// I think it's a shame if c++0x really gets rid of std::identity. It's soo useful!
template<typename> struct t2t { };

// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return call(t2t<Return>()); }

private:
  Value call(t2t<void>) { return Value(); }

  template<typename T>
  Value call(t2t<T>) { return Value((m_Host->*m_Method)()); }

private:
  Host       * m_Host;
  MethodType   m_Method;
};

You could use Return and just specialize operator() handling. No need to duplicate the whole template.

// I think it's a shame if c++0x really gets rid of std::identity. It's soo useful!
template<typename> struct t2t { };

// Specialization for signatures with no parameters
template< typename Host, typename Return >
class Method< Host, Return () >
{
public:
  typedef Return (Host::*MethodType)();
  Method( Host * host, MethodType method ) : m_Host(host), m_Method(method) {}

  Value operator()() { return call(t2t<Return>()); }

private:
  Value call(t2t<void>) { return Value(); }

  template<typename T>
  Value call(t2t<T>) { return Value((m_Host->*m_Method)()); }

private:
  Host       * m_Host;
  MethodType   m_Method;
};
薔薇婲 2024-09-24 06:48:15

不,绝对没有办法传递void。这是语言中的不规则现象。

函数参数列表(void) 被翻译为()。 Bjarne 更喜欢后者而不是前者,并且不情愿地允许 C 约定作为一种非常有限的语法糖。您甚至不能替换 void 的 typedef 别名,并且您当然不能有任何其他参数。

我个人认为这是一个坏主意。如果您可以编写 void(expr),那么您应该能够“初始化”void 类型的匿名参数。如果您还可以编写一个具有任意数量的 void 参数的函数,那么就会有一种方法以未指定的顺序执行多个表达式,这会以某种方式表达并发性。

至于处理不同大小的参数列表(也称为可变参数),请在开始尝试学习 Boost 预处理器之前参阅 C++0x 中的可变参数模板。

No, there is absolutely no way to pass a void. It is an irregularity in the language.

The function argument list (void) is translated as (). Bjarne prefers the latter to the former, and begrudgingly allowed the C convention as a very limited kind of syntactic sugar. You can't even substitute a typedef alias of void, and you certainly can't have any other arguments.

I personally think this is a bad idea. If you can write void(expr), then you should be able to "initialize" an anonymous argument of type void. If you could also write a function with an arbitrary number of void arguments, there would be a way to execute a number of expressions in unspecified order, which would express concurrency in a way.

As for handling different-sized argument lists (also known as variadic), see variadic templates in C++0x before you start trying to learn Boost Preprocessor.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文