There are a couple of considerations here.
Suppose you want to parse the file from disk, then based off of the type on disk you want to create an object. You handle these objects in a mostly indistinguishable way. There are a finite list of types you are working with.
The answer I'd use for this would be a boost::variant to store the data, and a map (like Matthiew's answer) that maps the name of the type to a reader (or parser) for the type. The reader then returns a boost::variant<int, double, char, string, etc>.
Code then interacts with the variant in a pseudo uniform way. Helper functions use boost functions to call functors to interact with the variant.
Ie, something like this:
typedef boost::variant<int, double> myVariant;
typedef std::function< myVariant( input_stream_type& ) > ValueParser;
ValueParser GetParser( std::string typename );
// ...
struct DoTypeSpecificWork
{
typedef void result_type;
void operator()( int ) { /* ... int code goes here */ }
void operator()( double ) { /* ... double code goes here */ }
};
ValueParser parser = GetParser( propType );
myVariant var = parser( input_stream );
boost::variant::apply_visitor( DoTypeSpecificWork(), var );
Another option is to have a base PropertyBase class that has abstract interfaces that are type agnostic. Then Property<T> child classes that implement those abstract interfaces for each type. Creating those Property<T> child classes could be done directly (forcing the parser to know about your Property class), or indirectly (ie, you take a variant and produce an appropriate Property<T>, which decouples to parsing code from your type abstraction).
Basically, you need to decide between type erasure, type abstraction and template based programming to deal with multiple types. They all have their own advantages.
pCharafter the initalization?ifstatements in such a way that they'll always be true?