#c #templates #c 14 #c 17 #void
#c #шаблоны #c 14 #c 17 #void
Вопрос:
У меня есть функция, которая имеет параметр option типа T.
template<typename T>
void foo( const Tamp; t = T() )
{ t.stuff; }
Все это было хорошо, но теперь у меня есть сценарий, в котором T становится void
. В этом случае я ожидал, что пустая функция не будет работать. Единственное работоспособное решение, которое у меня есть, требует трех отдельных объявлений, и у меня есть много таких методов:
template<typename T>
void foo( const Tamp; t)
{ t.stuff; }
template<typename T>
inline void foo()
{ foo(T()); }
template<>
inline void foo<void>() {}
В идеале я надеюсь, что должно быть более элегантное решение для перегрузки функции ‘Void’, не прибегая к 3-му объявлению? Особенно с новым C 17, решающим так много задач в наши дни! Более краткий синтаксис, который был бы короче, мог бы быть приятным…
Комментарии:
1. Если
T
естьvoid
, чтоt.stuff()
предполагается делать? Похоже, нам нужна ошибка компилятора. Вы действительно хотите, чтобы вызовfoo
был неоперабельным? Если это так, возможно, нам следует пересмотреть ваш дизайн.
Ответ №1:
Более простое решение (в том смысле, что существует только 2 перегрузки) было бы примерно таким:
template<typename T>
void foo( const Tamp; t = T() ) {
t.stuff;
}
template<typename T>
std::enable_if_t<std::is_void_v<T>>
foo() {}
// void foo(const Tamp;); is the only viable overload for non-void T,
// since std::enable_if_t SFINAEs
// void foo(); is the only viable overload for void T,
// since const Tamp; forms a reference to void
И это можно немного сократить с помощью шаблонов псевдонимов, поскольку вы часто используете этот шаблон:
template<typename T, typename TypeIfVoid = void>
using if_void = std::enable_if_t<std::is_void_v<T>, TypeIfVoid>;
template<typename T>
void foo(const Tamp; t = T()) {
t.stuff;
}
template<typename T>
if_void<T> foo() {}
Комментарии:
1. Вторая версия действительно подходит для варианта использования, имеющего много функций. Я пытался добиться этого, но мой синтаксис был неправильным, поэтому спасибо за помощь в решении этой проблемы 🙂 Есть и другие ответы с одним определением функции, но все они остаются очень подробными для чтения использования, поэтому я приму этот ответ, поскольку он ясен и лаконичен. Спасибо
Ответ №2:
Это сделают два параметра шаблона по умолчанию:
template<class T> using FallbackT = std::conditional_t<std::is_void_v<T>, int, T>;
template<class T = intamp;, class U = FallbackT<T>>
void foo(U constamp; t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
intamp;
значение по умолчанию для T
so, чтобы компиляция завершилась неудачно (по умолчанию создается ссылка на U()
), если кто-то попытается вызвать foo()
без аргумента шаблона или фактического аргумента (попробуйте раскомментировать его в примере).
Я использую int
внутри FallbackT
шаблона псевдонима, поскольку U
просто должно быть что-то, что можно сконструировать по умолчанию — это не видно пользователю.
Если вы хотите быть фантазийным (и предотвратить неправильное использование), вы можете добавить защиту от переменных и использовать типы замыканий:
template<
class T = decltype([]{})amp;,
class...,
class U = std::conditional_t<std::is_void_v<T>, decltype([]{}), T>>
void foo(U constamp; t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Здесь защита переменных предотвращает явное указание U, как, например foo<int, long>()
; типы замыкания делают невозможным вызов foo
с этими типами любыми другими способами — это, вероятно, не нужно.
Комментарии:
1. Спасибо за отличный ответ. Первая версия решает прецедент, однако она довольно подробная для применения ко многим функциям. Это было бы хорошо, если бы существовал синтаксис, который мог бы сократить сложность копирования-вставки и последующие возможности ошибок! Для «причудливой» версии требуются функции -std = C 2a, приятно видеть, но я не совсем понимаю, какого неправильного использования это могло бы избежать?
2. Конечно, я сократил его настолько, насколько считаю возможным — вам понадобится какой-то шаблон псевдонима для второго параметра шаблона. Я также добавил объяснение «необычных» функций / методов.
Ответ №3:
В идеале я надеюсь, что должно быть более элегантное решение для перегрузки функции ‘Void’, не прибегая к 3-му объявлению? Особенно с новым C 17, решающим так много задач в наши дни! Более краткий синтаксис, который был бы короче, мог бы быть приятным…
Хорошо… без 3-го объявления, да (вы можете использовать только один).
Более элегантный… я полагаю, это вопрос вкуса.
Более совпадающий синтаксис … хорошо… я полагаю, почти то же самое.
В любом случае, я предлагаю следующую версию if constexpr
и std::conditional_t
основываюсь.
template <typename T,
typename U = std::conditional_t<std::is_same_v<T, void>,
int,
T>>
void foo (U const amp; u = U{})
{
if constexpr ( std::is_same_v<T, void> )
{ /* do nothing */ }
else
{ /* do u.stuff; */ }
}
Комментарии:
1. Единственная проблема с этим заключается в том, что это
T
должно быть предоставлено (иU
может быть выведено на что-то другое). Конечно, не проблема, если она никогда не вызывается какfoo(value);
илиfoo<base_class>(derived_object);