Возьмите следующий минимальный пример:
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>
и я получаю ошибку.
Краткий ответ: «шаблоны не являются подстановкой строк». void f(void)
имеет значение только постольку, поскольку void f()
в C ++ для обратной совместимости с 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
является
эквивалентно пустому списку параметров. За исключением этого особого случая, параметр не должен иметь тип резюмеvoid
,
В вашем Type2
Например, T
в void(T)
это зависимый тип—это зависит от параметра шаблона.
Когда объявлена функция, принимающая параметр типа void
, как в std::function<void(void)>
это действительно глупый способ сказать, что он принимает нулевые параметры. Но способ, которым вы объявили Type2 как std::function
с подписью, которая ничего не возвращает (void), но принимает 1 параметр. void — это не тип, который можно использовать в качестве параметра, это просто способ объявить, что параметров нет. Так что он не работает с Type2, потому что для этого требуется фактический тип, который можно использовать в качестве параметра.
Void можно интерпретировать как пустой параметр, если вы передадите его функции. Вы не используете пустой указатель в конце концов, так
void func (void)
становится
void func ()