7

いくつかのメソッドを持つターゲット クラスがあるとします。

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();

上記と同様のエラーが発生します。

1 つの解決策は、メソッドを 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 戻り型の特殊化を追加したいと考えています。その複製作業が 2 倍になるだけです。

void 戻り型のこの 2 番目の特殊化を回避する方法はありますか?

4

2 に答える 2

4

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;
};
于 2010-09-11T11:20:18.253 に答える
2

いいえ、 を渡す方法は絶対にありませんvoid。それは言語の不規則性です。

関数の引数リスト(void)は として翻訳され()ます。Bjarne は前者よりも後者を好み、しぶしぶながら、非常に限られた種類のシンタックス シュガーとして C 規則を許可しました。の typedef エイリアスを置き換えることさえvoidできず、他の引数を持つこともできません。

私は個人的にこれは悪い考えだと思います。を書くことができればvoid(expr)、タイプの無名引数を「初期化」できるはずですvoid。任意の数の引数を持つ関数を書くこともできればvoid、いくつかの式を不特定の順序で実行する方法があり、それはある意味で並行性を表現します。

さまざまなサイズの引数リスト (可変個引数としても知られる) の処理については、Boost プリプロセッサの学習を開始する前に、C++0x の可変個引数テンプレートを参照してください。

于 2010-09-06T03:48:00.810 に答える