void возвращаемое значение из функции, используемой в качестве входных данных для шаблонной функции, рассматривается как параметр - PullRequest
6 голосов
/ 06 сентября 2010

Скажем, у вас есть целевой класс с некоторыми методами:

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 ) {}
};

И я хочу создать объект Value, используя метод из класса Subject:

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;
};

Использование этого класса Method для метода, который возвращает что-то (а именно intReturn), будет выглядеть так:

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;
};

Помимо того, что это просто уродливо, я также хочу специализировать класс Method для X чисел параметров подписи, что уже включает в себя многократное дублирование кода (надеюсь, Boost.Preprocessor может помочь здесь), а затем добавление специализации Пустые типы возвращаемых данных только удваивают усилия по дублированию.

Есть ли способ избежать этой второй специализации для типов возврата void?

Ответы [ 2 ]

4 голосов
/ 11 сентября 2010

Вы можете использовать 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;
};
2 голосов
/ 06 сентября 2010

Нет, нет абсолютно никакого способа передать void.Это нарушение языка.

Список аргументов функции (void) переводится как ().Бьярне предпочитает последнее первому и с неохотой допускает использование конвенции С в качестве очень ограниченного вида синтаксического сахара.Вы даже не можете заменить псевдоним typedef на void, и у вас точно не может быть никаких других аргументов.

Лично я считаю, что это плохая идея.Если вы можете написать void(expr), то вы сможете «инициализировать» анонимный аргумент типа void.Если бы вы могли также написать функцию с произвольным числом void аргументов, был бы способ выполнить несколько выражений в неуказанном порядке, который бы выражал параллелизм таким образом.

Что касается обработки различныхсписки аргументов (также известные как variadic), посмотрите шаблоны variadic в C ++ 0x, прежде чем пытаться изучать Boost Preprocessor.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...