Использование аргументов шаблона '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 ()