tags:

views:

855

answers:

9

Hello, I'm working in C++ and I need to know if a scalar value (for instance a double) is "defined" or not. I also need to be able to "undef" it if needed:

class Foo {
public:
    double get_bar();

private:
    double bar;
    void calculate_bar() {
        bar = something();
    }
};

double Foo::get_bar() {
    if ( undefined(bar) )
        calculate_bar();
    return bar;
}

Is it possible in C++?

Thanks

+2  A: 

If you mean at run-time, there is no such thing. If bar is never initialized, it will have whatever random bits happen to be there, depending on how the object is allocated (some allocators will initialize new memory to all-zero).

edit: it's up to the programmer to handle object state in constructors and/or manual initialization methods like init()

Jason S
+1  A: 

C++ does not have an "undefined" state for primitive types. The closest available for float/double would be NAN, but that really has a different meaning.

Darron
I've used a quiet NaN (qNaN) for this purpose myself: http://en.wikipedia.org/wiki/NaN#Quiet_NaNs. The IEEE encoding leaves lots of undefined bits in a NaN, allowing you to distinguish between NaN as a calculation result versus an explicitly assigned value.
Trevor Robinson
+1  A: 

This is not possible in C/C++, primitives will always a value assigned (mostly garbage, whatever was on that spot in memory before it, unless explicitly assigned at declaration). I's common to have a placeholder value (i.e. 0 for pointers) which denotes not-used, however these have to be explicitly assigned as well. If your double can take on any value, then I suggest you put a boolean next to it, assigned to false initially, and test/set that one when you want to do your calculation.

roe
+7  A: 

As the other answers says, C++ doesn't have this concept. You can easily work around it though.

Either you can have an undefined value which you initialize bar to in the constructor, typically -1.0 or something similar.

If you know that calculate_bar never returns negative values you can implement the undefined function as a check for < 0.0.

A more general solution is having a bool saying whether bar is defined yet that you initialized to false in the constructor and when you first set it you change it to true. boost::optional does this in an elegant templated way.

This is what the code example you have would look like.

class Foo {
public:
    double get_bar();
    Foo() : barDefined(false) {}
private:
    double bar;
    bool barDefined;
    void calculate_bar() {
        bar = something();
    }
};

double Foo::get_bar() {
    if ( barDefined == false ) {
        calculate_bar();
        barDefined = true;
    }
    return bar;
}
Laserallan
boost::optional is truly interesting
Schildmeijer
What does everyone have against good old -1. Can't get no respect I say
Robert Gould
Robert Gould: it's a sentinel value. I'll leave it up to you to research why that's often considered a bad thing.
rmeador
I think -1 is fine if your assumptions on calculate_bar holds so you never get a correct value that's the same as your undefined value. I also think it's good practice to do something like:const double UNDEFINED = -1.0. Also checking for equality on floating point types can be treacherous.
Laserallan
If you can specify a given value as never happening, you can use it as a sentinel value, although I'd rather name it as Laserallan says. A quick check of the Standard shows no guarantee that "double f(2.3); return f == 2.3;" will return true; anybody know of problems in that area?
David Thornley
Should use Lazy initialization. Let the compiler do the work for you. Doing the work yourself is error prone (as it requires the programmer to think). Compiler will always do it correctly.
Martin York
For this example, your static based solution would be good, in a general situation where there can be many instances of Foo with different values for bar you need something more flexible.I think that there should at most one copy of the line in the "if(barDefined == false) {" in the class though :)
Laserallan
+1  A: 

Why not maintain a separate flag that gets initialized to false and then gets set to true when bar is calculated. It can then be 'undefed' by setting the flag to false again.

if(!isBarValid)
{
    calculateBar();
    isBarValid = true;
}
return bar;
Stephen Doyle
A: 

Initialize bar to some value which can never occur when you call the "something()"function in the constructor.

For example

Foo(): bar(-1) { }

Then check for the value -1 in the get bar function.

(hmmm Laserallan also posted that answer 1 minute before :-( ;-) )

RoccoD
Unfortunately my double can be any number.
tunnuz
+4  A: 

As others pointed out, there is nothing like an "undefined" state. But you may want to look into boost.optional

gimpf
A: 

You must do it by using an extra boolean.

To implement using an extra boolean, you could try logic like the following template:

template<typename T>
struct Defined
{
 bool defined;
 T value;
 Defined() : defined(false) {}
 Defined(const T& value_) : defined(true), value(value_) {}
 ... and perhaps other operators here ...
 ... to make this behave even more like a T ...
};
ChrisW
This is actually what boost::optional if you have access to boost in your program. Note that I learned about it in this thread :) http://stackoverflow.com/questions/412611/struct-with-boolean-field-default-initialization#412640
Laserallan
It's how C#'s `Nullable<T>` template is implemented too.
ChrisW
A: 

You could try the Construct on first use idiom and write get_bar() this way:

double & get_bar()
{
    static double *bar = new double(something());
    return *bar;
}

When you call get_bar() it will make bar for you if no one has asked for it yet. Any subsequent calls will just return bar. As the linked page says, this doesn't technically leak memory because the OS will reclaim it when the program exits.

UPDATE:

Changed the return value to double & to allow you to modify bar.

Kristo
Jason S
The next FAQ in the list answers that very question. :)http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
Kristo