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

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

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/c++11 / libc++, 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> и я получаю ошибку.

5 ответов


короткий ответ: "шаблоны не являются заменой строк". 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();
  }
};

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

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


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

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

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

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

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

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

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

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

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

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


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


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

void func (void)

становится

void func ()