c++ - template - Utilice explícitamente los valores predeterminados para algunos parámetros en la creación de instancias de plantillas de clases
template en c++ pdf (4)
No hay una forma de reutilizar los parámetros predeterminados directamente. Puede usar el comentario de Floris como una forma de proporcionar abreviaturas para usos comunes, pero el alias de la plantilla aún repetirá los valores predeterminados.
Alternativamente, la estructura de opciones se puede configurar para permitir el cambio de parámetros:
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>;
};
Y luego usarlo como
options<>::WithT1<int>::WithStringT<std::wstring>
Una plantilla de clase puede tener múltiples parámetros que tienen valores predeterminados.
template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string>
struct options;
Instalar la plantilla con solo los parámetros predeterminados es fácil:
options<> my_default_options;
Pero, ¿y si quiero cambiar un subconjunto de parámetros?
options<int, int, std::wstring> wstring_options;
No es obvio que int
es un valor predeterminado para el primer parámetro, mientras que para el segundo no lo es. ¿Hay algo como
options<default, int, std::wstring> wstring_options;
en C ++?
No, no hay nada en C ++ estándar que permita esto. Una opción, señalada por @FlorisVelleman en los comentarios, es introducir una plantilla de alias:
template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;
Esto tiene el inconveniente de tener que duplicar explícitamente el argumento predeterminado de UnderlyingT0
en la definición de alias, pero al menos se ''duplica en un solo lugar.
Muchas bibliotecas de Boost utilizan una opción alternativa. Introducen una etiqueta especial use_default
y la convierten en el valor predeterminado. Algo como esto:
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;
};
Aquí, las desventajas son que 1. no se pueden distinguir los argumentos predeterminados mirando la declaración de la plantilla, y 2. las options<>
y las options<int, long, std::string>
son tipos diferentes.
El primero se puede arreglar con una buena documentación, y este último probablemente pueda ser ayudado por el uso juicioso de las funciones de conversión y las clases base.
Si todos los argumentos de su plantilla tienen valores predeterminados como en su ejemplo, puede crear una estructura auxiliar para extraerlos por usted.
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;
Entonces úsalo así:
options<default_for<options,0>, int, std::string> o;
Me encontré con este problema de nuevo y se me ocurrió una versión más general de la solución de 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;
Ventajas:
- Selección flexible de parámetros para cambiar
- No requiere cambio de la clase original
- Admite clases que tienen algunos parámetros sin valores predeterminados
-
options<int,long,int>
ywith_n_t<2,int,options<>>
son del mismo tipo
Algunos ejemplos de uso:
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>
Podría generalizar más esto para tomar pares variados de índices y tipos para que no necesite anidar with_n_t
.