c ++ 11 — параметр функции шаблона C ++ с переменным значением по умолчанию

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

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

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

18

Решение

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

Но вы можете подделать это. Вы можете определить, что является последним типом в пакете. Если это SomeSpecialTypeВы можете запустить свой функционал. Если это не так 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))) );
}

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

17

Другие решения

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

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 хитрости для реализации функции.

4

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector