2

Is it possible to convert foo from float to long (and vice versa)?

auto foo = float(1234567891234.1234);
cout << "foo: " << foo << endl;

foo = long(1234567891234.1234);
cout << "foo: " << foo << endl;

The output is always:

foo: 1.23457e+12
foo: 1.23457e+12
ShoeLace
  • 3,476
  • 2
  • 30
  • 44
djent
  • 109
  • 2
  • 10
  • This isn't the problem, but don't use `std::endl` unless you need the extra stuff that it does. `'\n'` ends a line. – Pete Becker Jan 02 '17 at 17:09

1 Answers1

14

Not in the way you wrote it. First,

auto foo = float(1234567891234.1234);

uses auto type deduction rules to infer the type of the RHS, and the result is float. Once this is done, the type of foo is float and it is set in stone (C++ is statically typed, unlike e.g. Python). When you next write

foo = long(1234567891234.1234);

the type of foo is still float and it is not magically changed to long.

If you want to emulate a "change" of type you can at most perform a cast:

 cout << "foo (as long): " << static_cast<long>(foo) << endl;

or use an additional variable

long foo_long = foo; // again you may have a loss of precision 

but be aware of possible precision loss due to floating point representation.

If you have access to a C++17 compiler, you can use an std::variant<long, float>, which is a type-safe union, to switch between types. If not, you can just use a plain old union like

#include <iostream>

union Foo
{
    float f;
    long l;
};

int main()
{
    Foo foo;
    foo.f = float(1234567891234.1234); // we set up the float member
    std::cout << "foo: " << foo.f << std::endl;

    foo.l = long(1234567891234.1234); // we set up the long member
    std::cout << "foo: " << foo.l << std::endl;
}

Live on Coliru

Or, you can use a type-erasure technique like

#include <iostream>

int main()
{
    void *foo; // we will store the object via this pointer

    foo = new int{42};
    std::cout << *(int*)foo << '\n';
    operator delete(foo); // don't do delete foo, it is undefined behaviour

    foo = new float{42.42};
    std::cout << *(float*)foo << '\n';
    operator delete(foo); // don't do delete foo, it is undefined behaviour
}

Live on Coliru

The modern version of the code above can be re-written with a std::shared_ptr like

#include <iostream>
#include <memory>

int main()
{
    std::shared_ptr<void> foo{new int{42}};
    std::cout << *(int*)foo.get() << '\n';

    foo.reset(new float{42.42});
    std::cout << *(float*)foo.get() << '\n';
}

Live on Coliru

A std::unique_ptr<void> won't work as only std::shared_ptr implements type-erasure.

Of course, if you don't really care about storage size etc, just use 2 separate variables.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Is it possible to do this: foo = static_cast(foo); and have foo change to a long type? Is it possible to do that in some way? – djent Jan 02 '17 at 15:49
  • 4
    @djent No, as I said in the answer the type of `foo` will always be `float` for the duration of the program. You cannot under any circumstance change the type of a variable in C++. The closest you can come with is to use an [`std::variant`](http://en.cppreference.com/w/cpp/utility/variant) (C++17). – vsoftco Jan 02 '17 at 15:50
  • Thanks you've been a big help. I almost don't expect you to respond any furthur if you do see this. Is it possible to delete foo when it's a float then re-initialize it as long? – djent Jan 02 '17 at 17:19
  • 1
    @djent No, only if you use a union as in the example. If you just first declare it as `float`, it will stay `float` no matter what. In C++ the type of the variable is set up at compile time (that is, before the program is even run), and it will stay the same. You can use some technique called "[type erasure](http://stackoverflow.com/questions/34815513/what-is-type-erasure-in-c)" in which you declare a `void*` pointer then keep casting it to the required type, but try first to use the simplest and cleanest code. – vsoftco Jan 02 '17 at 17:21