A class template can have multiple parameters that all have defaults.
template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string>
struct options;
Instatiating the template with just default parameters is easy:
options<> my_default_options;
But what if I want to change a subset of parameters?
options<int, int, std::wstring> wstring_options;
It is not obvious that int
is a default for the first parameter while for the second it isn't. Is there something like
options<default, int, std::wstring> wstring_options;
in C++?
You cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.
No, there is nothing in standard C++ which would enable this. One option, noted by @FlorisVelleman in the comments, is to introduce an alias template:
template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;
This has the drawback of having to explicitly duplicate the default argument of UnderlyingT0
in the alias definition, but as least it' duplicated in one place only.
An alternative option is used by many Boost libraries. They introduce a special tag use_default
and make that the default value. Something like this:
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;
};
Here, the downsides are that 1. you cannot tell the default arguments by looking at the template declaration, and 2. options<>
and options<int, long, std::string>
are different types.
The former can be fixed by good documentation, and the latter can probably be helped by judicious use of conversion functions and base classes.
There isn't a way to reuse the default parameters directly. You can use Floris's comment as a way to provide shorthands for common uses, but the template alias will still repeat the defaults.
Alternatively, the options struct could be set up to allow switching out parameters:
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>;
};
And then use it as
options<>::WithT1<int>::WithStringT<std::wstring>
If all your template arguments have defaults like in your example, you could create a helper struct to extract them for you.
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;
Then use it like this:
options<default_for<options,0>, int, std::string> o;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With