Default parameters for methods of template class - c++

Is there a way to provide default parameter values for methods of a template class? For example I have the following:
template<class T>
class A
{
public:
A foo(T t);
};
How should I modify this to give foo a default parameter of type T? For example: T is int then a default value of -23, or T is char* then default value of "something", etc. Is this even possible?

If you want the default parameter to be just the default value (zero, usually), then you can write A foo(T t = T()). Otherwise, I suggest a trait class:
template <typename T> struct MyDefaults
{
static const T value = T();
};
template <> struct MyDefaults<int>
{
static const int value = -23;
};
template<class T>
class A
{
public:
A foo(T t = MyDefaults<T>::value);
};
Writing the constant value inside the class definition only works for integral types, I believe, so you may have to write it outside for all other types:
template <> struct MyDefaults<double>
{
static const double value;
};
const double MyDefaults<double>::value = -1.5;
template <> struct MyDefaults<const char *>
{
static const char * const value;
};
const char * const MyDefaults<const char *>::value = "Hello World";
In C++11, you could alternatively say static constexpr T value = T(); to make the template work for non-integral values, provided that T has a default constructor that is declared constexpr:
template <typename T> struct MyDefaults
{
static constexpr T value = T();
};
template <> struct MyDefaults<const char *>
{
static constexpr const char * value = "Hello World";
};

Related

How to implement a template provding different values for different tyes

I am using a library which define various constants, like JQX_TYPE_INT, JQX_TYPE_LONG, JQX_TYPE_DOUBLE etc.
My problem is to relate values to c++ types.
For example, in order to have a function which works for various types i have to implement a copy of the same code, differing only in the constant, for every c++-type, e.g.:
myObj *createObject(int iSize, int iDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_INT);
return new myObj(iRealSize);
}
myObj *createObject(int iSize, double dDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_DOUBLE);
return new myObj(iRealSize);
}
// ... same for float char, etc.
That is, apart from having to implement the same code several times, i have to use a dummy variable in order to use the constant related to the c++-type.
Is there some sort of template approach to do something like
template<tyepename T>
struct jqx_type {
static const int type;
// ... template magic
}
template<<typename T>
myObj *createObject(int iSize) {
int iRealSize = calcEffectiveSize(iSize, jqx_type<T>::type);
return new myObj(iRealSize);
}
I tried this
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
But this doesn't even compile.
Is there a way to achieve what i intend to do?
Here:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
You redeclared the same template several times with different default arguments. What you actually want is one template with different specializations:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
PS: type is very common name for member type aliases. I would not use it for an int. Maybe type_selector is a better name.
I would go with a much simpler
myObj *createObject(int iSize, int jqx_type) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject(16, JQX_TYPE_DOUBLE);
or if it needs to be constexpr
template <int jqx_type>
myObj *createObject(int iSize) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject<JQX_TYPE_DOUBLE>(16);
but lets assume you need the actual type it could be something like this:
template <typename T>
struct MyObj {
MyObj(std::size_t size, int jqx_type)
: iSize{calcEffectiveSize(size, jqx_type)} { ... }
MyObj * create(std::size_t size) {
constexpr int jqx_type = []() {
if constexpr (std::is_same_v<T, int>) return JQX_TYPE_INT;
if constexpr (std::is_same_v<T, double>) return JQX_TYPE_DOUBLE;
}();
return new MyObj{iSize, jqx_type};
}
};
or if you do want to go the type traits route then you have to specialize the template struct like this:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
But that's as much code as specializing the createObject function.
PS: you can match and mix ideas, too. E.g. template createObject function with the if constexpr lambda.

static function in template struct not using default template argument

I have this code which I wasn't able to compile and I was wondering if there's a way around it. Error is - Argument list for class template "a" is missing.
//not compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a::function(5);
return 0;
}
.
//compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a<int>::function(5);
return 0;
}
Why can it not use the template argument we passed on default and how can we fix that without entering the template parameter?
The default template parameter int can be used without specifying it, you just need to specify that a is a template:
int b = a<>::function(5);
// ^^
is there a way to do without a<>, just a?
In case your class template a is only intended to provide utility static functions and not act as an object (with state), you could use delegation via a function template, which returns a (dummy) a object, followed by using the fact that an object of a given type, say A, can invoke non-static as well as static member functions.
namespace detail {
template <typename T = int>
struct AImpl {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T AImpl<T>::x;
} // namespace detail
template<typename T = int>
constexpr detail::AImpl<T> a() { return {}; }
int main() {
const auto b_int = a().function(5);
const auto b_char = a<char>().function('a');
(void)b_int; (void)b_char;
}
If you in fact always wants to use deduction and never actually specify the type of the type template parameter (when other than int), you could exchange class template and its static data member and member function by a single function template that wraps a variable with static storage duration:
#include <type_traits>
template<typename T>
T function(T number) {
static T x;
return x = number;
}
int main() {
const auto b_int = function(5);
const auto b_char = function('a');
static_assert(std::is_same_v<decltype(b_int), const int>, "");
static_assert(std::is_same_v<decltype(b_char), const char>, "");
(void)b_int; (void)b_char;
}
This would be an entirely different (and more implicit) API, however.

Creating a template with default arguments based on type

I wanted to create a template for numerical values and for a given type I would like to provide default values for the min and max values.
I found a solution that works so far...
template<typename T> struct MinDefaultValue;
template<typename T> struct MaxDefaultValue;
template<typename T, usize_t MinDefault = MinDefaultValue<T>::value, usize_t MaxDefault = MaxDefaultValue<T>::value>
class NumericColumn
{
public:
public:
NumericColumn(T *pAddress, T nDefault, usize_t nMinValue = MinDefault, usize_t nMaxValue = MaxDefault)
{
mAddress = pAddress;
mDefault = nDefault;
mMinValue = nMinValue;
}
bool toValue(void)
{
return true;
}
private:
T *mAddress;
usize_t mMinValue;
usize_t mMaxValue;
T mDefault;
};
template <> struct MinDefaultValue<byte_t> { static const usize_t value = (usize_t)CHAR_MIN; };
template <> struct MaxDefaultValue<byte_t> { static const usize_t value = (usize_t)CHAR_MAX; };
The reason why I don't like it is, because now Min- and MaxDefaultValue is outside the class namespace, while it belongs inside it.
The problem I have is that I don't know how to define the specializations for the various types, because the parent class itself is also a template.
I changed the class to this:
template<typename T>
class NumericColumn
{
public:
template<typename T> struct MinDefaultValue;
template<typename T> struct MaxDefaultValue;
public:
NumericColumn(T *pAddress, T nDefault, usize_t nMinValue = MinDefaultValue<T>::value, usize_t nMaxValue = MaxDefaultValue<T>::value)
{
mAddress = pAddress;
mDefault = nDefault;
mMinValue = nMinValue;
}
bool toValue(void)
{
return true;
}
private:
T *mAddress;
usize_t mMinValue;
usize_t mMaxValue;
T mDefault;
};
But when I try to provide the spezialisations I get a compiler error:
template <> struct NumericColumn<byte_t>::MinDefaultValue<byte_t> { static const usize_t value = (usize_t)CHAR_MIN; };
template <> struct NumericColumn<byte_t>::MaxDefaultValue<byte_t> { static const usize_t value = (usize_t)CHAR_MAX; };
'class' : invalid or missing type parameter list
I think your second approach isn't quite what you mean it to be. Having MinDefaultValue and MaxDefaultValue as inner template classes means that every NumericColumn<T> template instantiation will have a separate template class. So NumericColumn<int>::MaxDefaultValue<int> is a separate type from NumericColumn<float>::MaxDefaultValue<float>.
Maybe what you want is a simple static const member which you specialize for each type:
template<typename T>
class NumericColumn
{
public:
static const std::size_t MinDefaultValue;
static const std::size_t MaxDefaultValue;
public:
NumericColumn(T *pAddress, T nDefault,
std::size_t nMinValue = MinDefaultValue,
std::size_t nMaxValue = MaxDefaultValue)
: mAddress{pAddress}, mDefault{pDefault},
mMinValue{nMinValue}, mMaxValue{mMaxValue}
{ }
};
template<>
const std::size_t NumericColumn<int>::MinDefaultValue = 1;
template<>
const std::size_t NumericColumn<int>::MaxDefaultValue = 10;
Note that if you don't define member specializations for some NumericType specialization, you'll get a link error when that is instantiated. Maybe that's what you want, maybe it isn't.
Here's a possible way to get a better error message at the expense of some brevity:
template <typename T>
struct dependent_false : std::false_type {};
template<typename T>
class NumericColumn
{
public:
struct MinDefault{
static_assert(dependent_false<T>::value,
"T does not have a minimum default defined");
};
struct MaxDefault{
static_assert(dependent_false<T>::value,
"T does not have a maximum default defined");
};
public:
NumericColumn(T *pAddress, T nDefault,
std::size_t nMinValue = MinDefault::value,
std::size_t nMaxValue = MaxDefault::value)
: mAddress{pAddress}, mDefault{pDefault},
mMinValue{nMinValue}, mMaxValue{mMaxValue}
{ }
};
template<>
struct NumericColumn<int>::MinDefault {
static constexpr std::size_t value = 1;
};
template<>
struct NumericColumn<int>::MaxDefault {
static constexpr std::size_t value = 10;
};
I don't think you can specialize the template inside the template class. I expect someone who knows the c++ standard better than I can chime in on that.
You could maybe use an access shim:
template <typename TColumn, typename T> inline T MinDefaultValue();
// specialization:
template <> inline byte_t MinDefaultValue<NumericColumn<byte_t>,byte_t> () {
return 5;
}
You can probably reduce this down by putting a typedef in your column type:
typedef T value_type;
And then something along the lines of:
template <typename TColumn> inline typename T::value_type MinDefaultValue();
// specialization:
template <> inline byte_t MinDefaultValue<NumericColumn<byte_t> > () {
return 5;
}
This then does then allow specialization of the min value for the column class type.
This will fail if there is no specialization.

std::initializer_list value as template parameter

I am trying to do something like this:
template<typename enumType,
std::initializer_list<enumType> values,
std::initializer_list<std::string> mappings>
struct enum_converter {
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
};
I want to use it as follows:
enum test_enum {value_a, value_b};
struct test_enum_converter : public enum_converter<
test_enum,
{value_a, value_b},
{"a", "b"}> {};
GCC tells me:
class std::initializer_list<_Tp> is not a valid type
for a template constant parameter.
Adding const to the type does not change anything. Is there a workaround or a similar solution?
Only integral types, enumerations, pointers and references are allowed as non-type template parameters. std::initializer_list is neither of those.
Do you need the values & mappings to be template parameters? How about making them normal parameters of the constructor, and keeping only the enum as a template param?
template<typename enumType>
struct enum_converter {
enum_converter(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings) : values(values), mappings(mappings)
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
private:
std::initializer_list<enumType> values;
std::initializer_list<std::string> mappings;
};
enum_converter<test_enum> test_enum_converter({...}, {...});
int main()
{
test_enum_converter.toEnum("bla");
}
EDIT
Here is an instance-free alternative:
template<typename enumType>
struct enum_converter {
static init(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings)
{ s_values = values; s_mappings = mappings; }
static enumType toEnum(const std::string& literal) { ... }
static std::string toString(const enumType value) { ... }
private:
static std::initializer_list<enumType> s_values;
static std::initializer_list<std::string> s_mappings;
};
Just call init() once before using the class.
If you need more than one instantiation for a particular enumeration, you can add a disambiguation parameter to the template, like this:
template <typename enumType, typename tag>
struct enum_converter { /*as before*/ };
int main()
{
struct data_strings;
struct user_readable_strings;
enum_converter<test_enum, data_strings>::init({...}, {"a", "b"});
enum_converter<test_enum, user_readable_strings>::init({...}, {"Option A", "Option B"});
}
Pointers can only be template arguments if they are constant expressions, i.e. the address of an object with extern linkage. The following code works, though it's your call whether it's worth it:
typedef char const * charptype;
template <int, charptype> struct item;
template <typename ...> struct econv
{
static constexpr charptype convert(int) { return nullptr; }
};
template <int I, charptype S, typename ...Tail>
struct econv<item<I, S>, Tail...>
{
static constexpr charptype convert(int i)
{
return i == I ? S : econv<Tail...>::convert(i);
}
};
#include <iostream>
extern char const Hello[] = "Hello";
extern char const World[] = "World";
int main()
{
std::cout << econv< item<1, Hello>
, item<2, World> >::convert(2)
<< "\n";
}
By putting the strings into an anonymous namespace and using some helper macros, this approach might be cleaned up a bit to look presentable.

Call non-specialised template class function from specialized template class function

Is it possible to call a function defined in a non-specialised template class from a specialised template class? Here is an example of what i am attempting:
template <typename T>
struct Convert
{
static inline void toString(unsigned num, unsigned places, std::string& str) { ... }
};
template <>
struct Convert<int8_t>
{
static inline void toString(unsigned num, std::string& str)
{
Convert<int8_t>::toString(num, digitis(num), str);
}
};
GCC complains that it can't see the non-specialised class function; i.e. I guess it only looks within the specialised class.
Any thoughts?
EDIT
Here is a more concrete example from my code (with a possible solution):
struct NonSpecial { };
template <typename T>
class Convert
{
template <typename R>
static inline R fromString(const register char *str, const unsigned str_len)
{
R result = 0;
//convert str to R
return result;
}
friend class Convert<int8_t>;
friend class Convert<uint8_t>;
}
template <>
struct Convert<int8_t>
{
static inline int8_t fromString(const register char* str, const unsigned str_len = 4)
{
Convert<NonSpecial>::fromString<int8_t>(str, str_len);
}
};
template <>
struct Convert<uint8_t>
{
static inline uint8_t fromString(const register char* str, const unsigned str_len = 3)
{
Convert<NonSpecial>::fromString<uint8_t>(str, str_len);
}
};
I have other functions - toString(), countDigits(), etc. I have chosen this approach so I can keep the same function names for each type (i.e. don't need toStringU32(), toString32, etc.). I considered template specialization but I don't believe this is possible.
In general, this isn’t possible.
There are different possible solutions but they “cheat”. The first is to hoist off the actual default logic into a different function that is not specialized. Now you can call this function from both toString implementations.
The second alternative entails inheriting from the non-specialized class and passing a special tag as the template argument:
struct BaseClassTag { };
template <>
struct Convert<int8_t> : public Convert<BaseClassTag>
{
typedef Convert<BaseClassTag> TBase;
static inline void toString(unsigned num, std::string& str)
{
TBase::toString(num, digitis(num), str);
}
};