user3240688
user3240688

Reputation: 1327

Default value for Template Class, C++

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

Answers (4)

Henri Hein
Henri Hein

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

bolov
bolov

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

R Sahu
R Sahu

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.

  1. You can use the default_value approach as suggested in the other answer.

  2. 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

jfMR
jfMR

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

Related Questions