Reputation: 1327
Is there a way to give a default value for a template class?
E.g. Say I have a class TimeSeries
, that is templated. And I have a function called Foo()
that returns T
.
template <typename T>
class TimeSeries
{
T foo();
}
I want Foo()
to do some calculation and return something of type T
. But in case it can't, I want it to give a default value. If T
was double
, I want that to be NaN
. If T
was int
, I want that to be 0
.
TimeSeries<double> t;
t.foo(); // returns NAN
TimeSeries<int> t;
t.foo(); // returns 0
How would I achieve that?
A bad solution is for Foo()
to take in a default value.
template <typename T>
class TimeSeries
{
T foo(T defaultValue)
{
T ret;
// if OK
ret = computeValue();
// if not OK
ret = defaultValue;
return ret;
}
}
So default value would be 0
, or NaN
, depending on the user.
Or am I asking for a flawed design in the first place?
EDIT -
A logical question would be to ask what happens if T
is not int
or double
. What does a default value even mean?
The way I'm using TimeSeries
, T
is not exactly a general purpose type. It could only be 1 of int
, double
, and string
.
I thought about simply writing 3 separate TimeSeries
classes, but that seems like a flawed approach.
I'm open to other ideas.
Upvotes: 5
Views: 6649
Reputation: 71
I had a similar issue. In my case, I just had a situation where I wanted to initialize a variable of the template parameter type, not as a default return. I could not get any of the above methods to work. I used a variation on @jfMR's suggestion. (Posting as a separate answer for formatting). In my case, I also wanted to support both primitive types and object types.
I used a second template parameter to provide a callable struct producing the default value I need. Full sample:
#include <stdio.h>
#include <string>
#include <iostream>
struct DefaultInt
{
int operator () () const { return 42; }
};
struct DefaultChar
{
char operator () () const { return 'z'; }
};
struct DefaultString
{
std::string operator () () const { return std::string("waldo"); }
};
template<class TP, class TPDefaultValue> class DemoWrapper
{
public:
DemoWrapper()
{
TPDefaultValue valueFactory;
_wrap = valueFactory();
}
DemoWrapper(TP wrap) : _wrap(wrap)
{}
void Print() { std::cout << _wrap << std::endl; }
private:
TP _wrap;
};
int main(int argc, const char *argv[])
{
std::string demoString("Thanks for all the fish");
const int demoInt = 13;
DemoWrapper<int, DefaultInt> dw1(demoInt);
DemoWrapper<int, DefaultInt> dw2;
DemoWrapper<std::string, DefaultString> dw3(demoString);
DemoWrapper<std::string, DefaultString> dw4;
dw1.Print();
dw2.Print();
dw3.Print();
dw4.Print();
}
Output:
13
42
Thanks for all the fish
waldo
Upvotes: 0
Reputation: 75727
since C++14 you can use a variable template:
template <class T> constexpr T default_value = {};
template <>
constexpr double default_value<double> = std::numeric_limits<double>::quiet_NaN();
template <typename T> class TimeSeries
{
auto foo() -> T
{
T ret;
if (condition)
return ret;
else
return default_value<T>;
}
};
Upvotes: 4
Reputation: 206607
Or am I asking for a flawed design in the first place?
As far as I'm concerned it's a valid requirement.
I see two ways you can address that requirment.
You can use the default_value
approach as suggested in the other answer.
If you have the option of using C++17, you can use std::optional
.
If you don't have the option of using C++17, you can emulate std::optional
in a less sophisticated way by changing the return value to std::pair<boo, T>
with the understanding that the first
of the returned value is set to false
if the computation was not successful and to true
if the computation was successful.
Personally, I would prefer the second solution. It does not rely on sentry values. It captures the state of whether the computation was successful in a clear and explicit manner.
Upvotes: 3
Reputation: 24738
You could declare the following template, default_value<>
:
template<typename>
struct default_value;
Then, provide full specializations for int
and double
with a member called value
which provides the desired default value:
template<>
struct default_value<int> {
static constexpr int value = 0;
};
template<>
struct default_value<double> {
static constexpr double value = NaN;
};
Then, in your foo
function template, you can simply return that value:
return default_value<T>::value;
which will depend on T
.
Upvotes: 10