Подтвердить что ты не робот

Параметр функции вариационного шаблона С++ со значением по умолчанию

У меня есть функция, которая принимает один параметр со значением по умолчанию. Теперь я также хочу, чтобы он принял переменное количество параметров и пересылал их к какой-либо другой функции. Параметры функции со значением по умолчанию должны быть последними, поэтому... могу ли я поместить этот параметр после вариационного пакета, и компилятор определит, подаю ли я его или нет при вызове функции?

(Предполагая, что пакет не содержит тип этого последнего параметра. При необходимости мы можем предположить, что, поскольку этот тип обычно не должен быть известен пользователю, в противном случае он считается неправильным использованием моего интерфейса в любом случае....)

template <class... Args>
void func (Args&&... args, SomeSpecialType num = fromNum(5))
{
}
4b9b3361

Ответ 1

Нет, пакеты должны быть последними.

Но вы можете подделать его. Вы можете определить, что такое последний тип в пакете. Если это SomeSpecialType, вы можете запустить свой func. Если это не SomeSpecialType, вы можете рекурсивно называть себя своими перенаправленными аргументами и fromNum(5) прилагается.

Если вы хотите быть фантазией, эта проверка может быть выполнена во время компиляции (т.е. с другой перегрузкой) с использованием методов SFINAE. Но это, вероятно, не стоит хлопот, учитывая, что проверка "времени выполнения" будет постоянной при заданной перегрузке и, следовательно, почти наверняка будет оптимизирована, и SFINAE не следует использовать легко.

Это не дает вам подпись, которую вы хотите, но она дает вам поведение, которое вы хотите. Вы должны будете объяснить предполагаемую подпись в комментариях.

Что-то вроде этого, после удаления опечаток и т.п.:

// extract the last type in a pack.  The last type in a pack with no elements is
// not a type:
template<typename... Ts>
struct last_type {};
template<typename T0>
struct last_type<T0> {
  typedef T0 type;
};
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {};

// using aliases, because typename spam sucks:
template<typename Ts...>
using LastType = typename last_type<Ts...>::type;
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b, T>::type;
template<typename T>
using Decay = typename std::decay<T>::type;

// the case where the last argument is SomeSpecialType:
template<
  typename... Args,
  typename=EnableIf<
    std::is_same<
      Decay<LastType<Args...>>,
      SomeSpecialType
    >::value
  >
void func( Args&&... args ) {
  // code
}

// the case where there is no SomeSpecialType last:    
template<
  typename... Args,
  typename=EnableIf<
    !std::is_same<
      typename std::decay<LastType<Args...>>::type,
      SomeSpecialType
    >::value
  >
void func( Args&&... args ) {
  func( std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5))) );
}

// the 0-arg case, because both of the above require that there be an actual
// last type:
void func() {
  func( std::move(static_cast<SomeSpecialType>(fromNum(5))) );
}

или что-то в этом роде.

Ответ 2

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

template <class... Args>
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5))
{
  // don't forget to move t when you use it for the last time
}

Плюсы: интерфейс намного проще, перегрузка и добавление аргументов по умолчанию довольно просто.

Минусы: вызывающий должен вручную переносить аргументы в вызов std::make_tuple или std::forward_as_tuple. Кроме того, вам, вероятно, придется прибегнуть к трюкам std::index_sequence для реализации функции.