【问题标题】:void return value from a function used as input to a templated function is seen as a parameter用作模板化函数输入的函数的 void 返回值被视为参数
【发布时间】:2010-09-06 02:04:17
【问题描述】:

假设你有一些目标类,上面有一些方法:

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 返回类型的方法进行部分专门化:

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 返回类型进行第二次特化?

【问题讨论】:

  • +1。我认为这是一个写得很好的问题。

标签: c++ templates generic-programming


【解决方案1】:

您可以使用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;
};

【讨论】:

  • ... 摆脱 identity 的原因是,将其 operator() 定义为使用右值引用而不是 const&amp; 会破坏太多已安装的代码!
  • 是的,这就是我要找的!我记得不久前读过 Alexandrescu 关于 Type2Type 的文章,但后来忘记了。
【解决方案2】:

不,绝对没有办法传递void。这是语言的不规范。

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

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

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

【讨论】:

  • 谢谢,我害怕那个。我以为我在 boost.function 中看到了一些东西,他们正在做类似的事情,但显然不是。感谢关于可变参数模板的提示,不幸的是我还没有升级我的编译器。
  • "一个带有一个 void 类型参数的函数参数列表被调整为一个没有参数的列表。" -> 实际上它更加严格。在 C99 中是这样,但在 C++ 中,替换是纯粹的句法:标记序列“(void)”表示零参数列表。但例如“(Void)”,其中“Void”表示 void 类型在 C++ 中是非法的。
猜你喜欢
  • 1970-01-01
  • 2017-07-13
  • 2016-08-28
  • 2018-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
相关资源
最近更新 更多