Использование аргументов шаблона 'void' в С++

Возьмем следующий минимальный пример:

using Type1 = std::function<void(void)>;

template <typename T>
using Type2 = std::function<void(T)>;

Type1 whyDoesThisWork;
Type2<void> andYetThisDoesNot;

Если второй псевдоним типа, я получаю ошибку "Аргумент может не иметь" void "типа". (Я тестировал с Xcode 4.5, Clang/С++ 11/libС++, OS X 10.7.)

Мне это любопытно: я бы ожидал, что Type1 и Type2<void> будут вести себя одинаково. Что здесь происходит? И есть ли способ переписать псевдоним второго типа, чтобы я мог написать Type2<void> и получить std::function<void(void)> вместо ошибки?

Изменить. Я должен добавить, что причина, по которой я хочу, - это разрешить следующее:

template <typename ... T>
using Continuation = std::function<void(T...)>;

auto someFunc = []() -> void {
  printf("I'm returning void!\n");
};

Continuation<decltype(someFunc())> c;

Continuation<decltype(someFunc())> становится Continuation<void>, и я получаю ошибку.

Ответы

Ответ 1

Короткий ответ: "Шаблоны не являются заменой строк". void f(void) имеет смысл только в том случае, если он является псевдонимом для void f() в С++, чтобы быть обратно совместимым с C.

Первый шаг - использовать переменные, как указано в других местах.

Второй шаг заключается в том, как отобразить void возвращаемые функции... ну, может быть, что-то вроде std::function<void()> или, может быть, что-то еще. Я говорю, может быть, что-то еще, потому что, в отличие от других случаев, вы не можете вызвать std::function<void()> foo; foo( []()->void {} ); - это не настоящее продолжение.

Что-то вроде этого может быть:

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
};

то используйте его следующим образом:

auto someFunc = []()->void {};
Continuation<decltype(someFunc())>::type c;

который дает вам тип, который вы хотите. Вы можете даже добавить в заявку на продолжение:

template<typename T>
struct Continuation
{
  typedef std::function<void(T)> type;

  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    cont( f(args...) );
  }
};

template<>
struct Continuation<void>
{
  typedef std::function<void()> type;
  template<typename func, typename... Args>
  static void Apply( type const& cont, func&& f, Args... args)
  {
    f(args...);
    cont();
  }
};

который позволяет применять продолжение к выполнению функции равномерно, если входящий тип является недействительным или является непустым типом.

Однако я бы спросил: "Почему вы хотите это сделать"?

Ответ 2

У меня нет фактического ответа, только то, что я сказал в комментарии: у вас не может быть void как тип функции, как в:

int foo(int, char, void, bool, void, void);     // nonsense!

Я считаю, что T(void) разрешен только как обозначение совместимости для C (которое отличает объявления и прототипы, очень отличные от С++, и которые должны иметь возможность сказать "без аргументов" ).

Итак, решение должно быть переменным:

template <typename ...Args> using myType = std::function<void(Args...)>;

Таким образом, у вас не может быть аргументов:

myType<> f = []() { std::cout << "Boo\n"; }

Ответ 3

Несколько ответов уже объясняют обоснование. Чтобы добавить к этим ответам, в спецификации указано (С++ 11 §8.3.5 [dcl.func]/4):

Список параметров, состоящий из одного неназванного параметра не зависимого типа void, является эквивалентный пустому списку параметров. За исключением этого специального случая, параметр не должен иметь тип cv void.

В примере Type2 T in void(T) является зависимым типом - он зависит от параметра шаблона.

Ответ 4

Когда объявлена ​​функция для принятия параметра типа void, как в std::function<void(void)>, это действительно просто пустяковый способ сказать, что он принимает нулевые параметры. Но то, как вы объявили Type2, является std::function с сигнатурой, которая ничего не возвращает (void), но принимает один параметр. void не является типом, который может использоваться как параметр, это всего лишь способ объявить, что параметров нет. Поэтому он не работает с Type2, потому что для этого требуется фактический тип, который может использоваться как параметр.

Ответ 5

Void можно интерпретировать как пустой параметр, если передать его функции. Вы все равно не используете указатель void, поэтому

void func (void)

становится

void func ()