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

Явно использовать значения по умолчанию для некоторых параметров в создании экземпляра класса

Шаблон класса может иметь несколько параметров, все из которых имеют значения по умолчанию.

template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string>
struct options;

Устанавливать шаблон с помощью только стандартных параметров легко:

options<> my_default_options;

Но что, если я хочу изменить подмножество параметров?

options<int, int, std::wstring> wstring_options;

Не очевидно, что int является значением по умолчанию для первого параметра, а для второго - нет. Есть что-то вроде

options<default, int, std::wstring> wstring_options;

в С++?

4b9b3361

Ответ 1

Нет, в стандартном С++ нет ничего, что позволило бы это сделать. Один из вариантов, отмеченный в комментариях @FlorisVelleman, заключается в том, чтобы ввести шаблон псевдонима:

template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;

У этого есть недостаток в том, чтобы явно дублировать аргумент по умолчанию UnderlyingT0 в определении псевдонима, но как минимум он дублируется только в одном месте.

Альтернативный вариант используется многими библиотеками Boost. Они вводят специальный тег use_default и делают это значением по умолчанию. Что-то вроде этого:

struct use_default {};

template<typename UnderlyingT0 = use_default, typename UnderlyingtT1 = use_default, typename StringT = use_default>
struct options
{
  using RealUnderlyingT0 = typename std::conditional<
    std::is_same<UnderlyingT0, use_default>::value,
    int,
    UnderlyingT0
  >::type;

  using RealUnderlyingT1 = typename std::conditional<
    std::is_same<UnderlyingT1, use_default>::value,
    long,
    UnderlyingT1
  >::type;

  using RealStringT = typename std::conditional<
    std::is_same<StringT, use_default>::value,
    std::string,
    StringT
  >::type;
};

Здесь нижние части - это 1. вы не можете указать аргументы по умолчанию, посмотрев объявление шаблона, и 2. options<> и options<int, long, std::string> - разные типы.

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

Ответ 2

Невозможно напрямую использовать параметры по умолчанию. Вы можете использовать комментарий Floris как способ предоставления сокращений для общего использования, но псевдоним шаблона будет по-прежнему повторять настройки по умолчанию.

В качестве альтернативы можно настроить структуру параметров, чтобы можно было отключать параметры:

template <typename UnderlyingT0 = int,
          typename UnderlyingT1 = long,
          typename StringT = std::string>
struct options {
  template <typename NewT0>
  using WithT0 = options<NewT0, UnderylingT1, StringT>;
  template <typename NewT1>
  using WithT1 = options<UnderylingT0, NewT1, StringT>;
  template <typename NewStringT>
  using WithStringT = options<UnderylingT0, UnderylingT1, NewStringT>;
};

И затем используйте его как

options<>::WithT1<int>::WithStringT<std::wstring>

Ответ 3

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

template <class T, size_t N>
struct default_for_helper;

template <template <typename...> class T, size_t N, typename... Args>
struct default_for_helper<T<Args...>, N>
{
    using type = std::tuple_element_t<N, std::tuple<Args...>>;
};

template <template <typename...> class T, size_t N>
using default_for = typename default_for_helper<T<>, N>::type;

Затем используйте его следующим образом:

options<default_for<options,0>, int, std::string> o;

Ответ 4

Я снова столкнулся с этой проблемой и придумал более общую версию решения Sebastian Redl.

//given an index to replace at, a type to replace with and a tuple to replace in
//return a tuple of the same type as given, with the type at ReplaceAt set to ReplaceWith
template <size_t ReplaceAt, typename ReplaceWith, size_t... Idxs, typename... Args>
auto replace_type (std::index_sequence<Idxs...>, std::tuple<Args...>)
    -> std::tuple<std::conditional_t<ReplaceAt==Idxs, ReplaceWith, Args>...>;

//instantiates a template with the types held in a tuple
template <template <typename...> class T, typename Tuple>
struct type_from_tuple;

template <template <typename...> class T, typename... Ts>
struct type_from_tuple<T, std::tuple<Ts...>>
{
    using type = T<Ts...>;
};

//replaces the type used in a template instantiation of In at index ReplateAt with the type ReplaceWith
template <size_t ReplaceAt, typename ReplaceWith, class In>
struct with_n;

template <size_t At, typename With, template <typename...> class In, typename... InArgs>
struct with_n<At, With, In<InArgs...>>
{
    using tuple_type = decltype(replace_type<At,With>
        (std::index_sequence_for<InArgs...>{}, std::tuple<InArgs...>{}));

    using type = typename type_from_tuple<In,tuple_type>::type;
};

//convenience alias
template <size_t ReplaceAt, typename ReplaceWith, class In>
using with_n_t = typename with_n<ReplaceAt, ReplaceWith, In>::type;

Преимущества:

  • Гибкий выбор параметров для изменения
  • Не требует изменения исходного класса
  • Поддерживает классы, которые имеют некоторые параметры без настроек по умолчанию
  • options<int,long,int> и with_n_t<2,int,options<>> - это тот же тип

Некоторые примеры использования:

with_n_t<1, int, options<>> a; //options<int, int, std::string>
with_n_t<2, int,
   with_n_t<1, int, options<>>> b; //options<int, int, int>

Вы можете еще больше обобщить это, чтобы принимать вариативные пары индексов и типов, так что вам не нужно вставлять with_n_t.