Click here to Skip to main content
15,396,239 members
Please Sign up or sign in to vote.
3.00/5 (3 votes)
See more:
I'm having trouble wrapping my head around this and Mr. Google doesn't seem to know much about it either or I'm just not querying correctly, probably he latter but I've got a template;

C++
template<class T> class CSmartArray
{
   public:
     CSmartArray(...);

      blah...blah...blah

   private:
      T& m_preset_value;  //<=== The value to set
};

template <class T> T& CSmartArray<T>CSmartArray(...)
{
   //How do I set the following value to some default value such as zero?
   m_preset_value = 0;     //Nope
   m_preset_value = (T)0;  //Nope
   m_preset_value = (T&)0; //Nope
}


Question is how do I set the m_preset_value to some default value such as zero?

It's probably very simple but am not seeing it but how do I set any value to m_preset_value?

Urgent send code plz...... :)

Thanks Y'all

BTW Templates using GCC is a PITA!
Posted
Updated 13-Aug-13 8:08am
v3
Comments
Sergey Alexandrovich Kryukov 13-Aug-13 13:58pm
   
What code? What are you asking about? What are you trying to achieve?
—SA
H.Brydon 13-Aug-13 17:37pm
   
Good question (with perhaps confusing details per the comments and solution text).

+5 to offset whomever downvoted you.
Mike Hankey 13-Aug-13 17:55pm
   
Thanks, looks like I set off a fire storm?

As an aside, down voting only matters if you care. :)
Philippe Mori 13-Aug-13 19:12pm
   
By the way, even though I have already provided a solution, I just noticed that the class is named CSmartArray.

First, why do you need such a class when there is already a std::vector class?

Second, even you still want to use that class and are satisfied with using default value of T, then why not used std::vector for the implementation as it already handle expanding the array with default value of T.
Mike Hankey 13-Aug-13 19:33pm
   
Zero, I thought this would be a generic question as GCC now supports C++11 anyway;

First, I am developing this for an embedded system using GCC.

Second, refer to first!

If this were to be developed using VS I would not be having this problem but GCC error messages are so vague that sometimes they have nothing to do with the problem at hand so it's been a PITA to say the least but I'm starting to catch on and eye the nuances.

Thanks for your feedback!

This worked for me (in VS):

C++
template<class T> class CSmartArray
{
   public:
     CSmartArray();

   private:
      const T& m_preset_value;
};

template <class T> CSmartArray<T>::CSmartArray() : m_preset_value(0)
{
   
}


Not sure if it's quite what you want though. (Note I had to make it a const reference. You might just want to use a pointer here instead, it will probably be easier.)
   
Comments
Sergey Alexandrovich Kryukov 13-Aug-13 14:59pm
   
This is not really a solution, because you simply create a reference but provide no a way to really initialize the value of unknown type.
This is not so trivial thing with templates. Please see my answer where I discuss it.
—SA
If we take a look at the generated code then a reference is exactly the same as a pointer. On language level syntactically references have more strict rules: a reference has to be initialized exactly once where you declare/define it. If the reference is the member variable of a class then you have to initialize it from the initializer list of every constructor of the class. A reference can not be changed later to point to something else. After declaration and initialization the identifier of the reference variable works as if it was the identifier of the object to which the reference points. For this reason initializing a reference to zero has no point. If you want to change the reference after initialization to point to something else then you want to use a pointer instead of a reference.
   
Comments
Mike Hankey 13-Aug-13 14:39pm
   
Excellent I had forgotten about the initialize once rule and you hit it right on the head. I've not worked much with templates and it's been a learning experience.
Thanks!
pasztorpisti 13-Aug-13 14:49pm
   
You are welcome!
Sergey Alexandrovich Kryukov 13-Aug-13 15:02pm
   
This is all correct but actually does not solve the problem of initialization of the value itself, the value of the unknown type.
Ideally, this problem could be solved with, say, template parameter constraints.
I discuss this problem in my answer and provide a really simple work-around, please see.
—SA
It does not make much sense to have a reference member to an "unknown" type. If this is really what you want to do (assuming that a default constructor exists), you would also have to implement the destructor and ensure that the object is not copyable...

If not, then you have to ensure that the type is a POD type (rules are a bit different with C++ 11 but since I don't know all details).

Thus, you would have something like this (for simplicity all functions are defined inline):
C++
template<class T> class CSmartArray
{
public:
  CSmartArray() : m_preset_value(*new T())
  {
  }

  ~CSmartArray()
  {
    delete &m_preset_value;
  }

private:
  CSmartArray(const CSmartArray &); // No copy
  CSmartArray& operator=(const CSmartArray &); // No copy

  T& m_preset_value;
};


If an instance is used, then it simplify to this (this is what I would recommand to do):
C++
template<class T> class CSmartArray
{
public:
  CSmartArray() : m_preset_value() // Empty () will force default value in that case since a long time.
  {
  }

private:
  T m_preset_value;
};


By the way, this is not very flexible as it is not possible to create object not using the default constructor.
   
v2
What you are trying to achieve does not make sense, because the template parameter is any arbitrary type, and an arbitrary file does not have a concept of "null", but it does not even have to support a concept of some "default initial value". Nothing can be assumed for this type.

There are some work-around approaches. Let's think.

It may seem that you can internally use the pointer to T as your private member m_preset_value. Then you could assign this member to the 0 pointer, regardless of the actual type of T. However, it would not solve problem, only delay it, as you don't know the constructor for T if it is required, so how one would instantiate it?

The simplest real resolution could be something like this:
C++
template <class T> class SmartArray {
protected:
	T m_preset_value;
	virtual void Initialize() = 0;
public:
	void SomeMethod() { m_preset_value = Initialize(); } // could be anything, for example, constructor
};


This way, with each instantiation of the template you should also create a derived class and the virtual method should be overridden:
C++
class BooleanSmartArray : public SmartArray<bool> {
protected:
    virtual void Initialize() { m_preset_value = 0; }
};


This may seem to be not very convenient. As an alternative, you would need to use something like generic constraints of CLI (.NET, in particular). This way, you could create some interface class with operations like Initialize and more. Then, instead of arbitrary unconstrained type T, you could indicate that the template parameter is contrained to be derived from this interface class.

You can see these suggestions by Bjarne Stroustrup:
http://www.stroustrup.com/bs_faq2.html#constraints[^].

Another approach is implemented in boost: http://www.boost.org/doc/libs/1_36_0/libs/concept_check/concept_check.htm[^].

See also:
http://en.wikipedia.org/wiki/Boost_%28C%2B%2B_libraries%29[^],
http://www.boost.org/[^].

And if you can use C++11, you can think about the use of template std::initializer_list:
http://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists[^]

—SA
   
v2
Comments
pasztorpisti 13-Aug-13 15:34pm
   
This time I must disagree, I think you try to solve a completely different problem. When you store a pointer or reference in a template to an unknown type you usually initialize that reference or pointer with another reference/pointer your receive as a constructor/method parameter so you don't have to know the type. On the other hand in C++ you can expect the type to have a default (or any other kind of parametrized) constructor so you can create instances of that type locally (in contrast for example to java where they solved templates with a very stupid type erasure for some reason). On the other hand you have another serious mistake in your solution: calling a virtual method from a constructor is basically a bug in C++. However you can aid it with a trick by introducing an additional template parameter that must be the derived type, this trick also eliminates the cost of virtual method call:

template <class T, class Derived> class SmartArray {
protected:
T m_preset_value;
public:
void SomeMethod() { m_preset_value = static_cast<Derived*>(this)->Initialize(); } // could be anything, for example, constructor
};

class MyClass : public SmartArray<int, MyClass>
{
void Initialize() {
...

Initialize() can be normal static function. Some info for those interested: This is called "static polymorphism" in C++.
Sergey Alexandrovich Kryukov 13-Aug-13 15:43pm
   
If it's static, how can you provide specialized code per concrete type? Ultimately, you would need to assign 0 for one type and something else for another.

Even if OP does not want such initialization, this is a real problem. It actually depends on what the class does. The method like this initialization maybe not required. For example, if the template class is a container, it only make sense to transparently pass data from the user.

—SA
pasztorpisti 13-Aug-13 15:54pm
   
I solved such problems with type traits. One such template I often use is my TAutoHandle template. It works like an auto_ptr/unique_ptr but it stores different kind of handles (like FindFirstFile handle, socket handle, ...). In case of handles you must be able to tell which value is invalid (zero or -1?) you have to tell how to close it (FindClose() or closesocket(), ...).

template <typename T, typename Traits=ItemTraits<T> > class SmartArray {
public:
SmartArray()
{
Traits::Initialize(m_preset_value);
}
private:
T m_preset_value;
};

// default type traits implementation
template <typename T> class ItemTraits {
public:
static void Initialize(T& val) {
val = 0;
}
// TODO: add any other type specific operation or constant
};

struct SComplexType
{
int xxx;
};

template<> class ItemTraits<SComplexType>
{
public:
static void Initialize(SComplexType& t) {
// TODO
}
};

class SocketItemTraits {
public:
static void Initialize(SOCKET& sock) {
sock = INVALID_SOCKET;
}
};



int run()
{
SmartArray<SComplexType> ct;
SmartArray<bool> bt;
// I didn't use template specialization for type traits here because many handle types are
// typedeffed to void* so all of them would match the same ItemTraits sepcialization.
SmartArray<SOCKET, SocketItemTraits> st;
Sergey Alexandrovich Kryukov 13-Aug-13 16:30pm
   
That's the different stuff, would make a really excellent answer.
—SA
pasztorpisti 13-Aug-13 17:24pm
   
Thank you! But if OP is okay with the simple answer about reference initialization...
Sergey Alexandrovich Kryukov 13-Aug-13 17:27pm
   
I think this solution only postpones the problem...
—SA
pasztorpisti 13-Aug-13 17:31pm
   
Well, then I misunderstood you. If you write a class that you want to store in your array then all you have to do is writing an ItemTraits specialization for your class if the default one isn't good for you. Another benefit is that this specialized type trait doesn't have to be next to your array implementation in a central library, it can be next to your class. It think this is a perfect solution in many cases. What do you mean on postponing?
pasztorpisti 13-Aug-13 17:41pm
   
You can also write a default type trait that delegates the calls to the static methods of class you want to use in the array. This way the Initializer code is more coupled with the class and you can still create specialized type trait classes for primitive types that cant have static methods. - Thinking it over this is probably a worse solution than the separate type trait I usually use.
Sergey Alexandrovich Kryukov 13-Aug-13 18:06pm
   
Exactly; this is all about loose vs. strong coupling. In this respect, one of the best solution is the generic type constraint, but in CLI it was enormously spoiled by the superstitious decision to treat primitive types (and, to certain extent, other value types) as second-class types, and, due to that, such constraints became impossible. Generally, this is a big limitation or hassle for both C++ templates and CLI generics...
—SA
pasztorpisti 13-Aug-13 18:31pm
   
You are right, handling of primitive types has always been a big problem in both languages. I'm not a big template fan anyway, I use them only in some basic core libraries. Usually most of my library and application code is free of them for several reasons.
Sergey Alexandrovich Kryukov 13-Aug-13 18:56pm
   
If they were more comprehensive, I would use them more...
—SA
Philippe Mori 13-Aug-13 19:06pm
   
Well it is possible to initialize a type to its default value using parenthesis (see my solution) since a long time as it is required for STL (for exemple vector<>::resize).

Even though it is a possible solution, I do not recommand using derived classes for the purpose of overriding default value. For something like an array, I think that default should normally be sufficient. If values other than default need to be used, then it should be done on insertion (same as std::vector)
Sergey Alexandrovich Kryukov 13-Aug-13 19:11pm
   
It won't make any difference at all. It's possible in your case just because you use the reference, which is nullable, but it totally leaves aside the problem is initialization not the reference but the referenced object itself, in case of unknown type. And here we go again.
—SA
Philippe Mori 13-Aug-13 19:25pm
   
new T() create a pointer to an object initialized with default value of T and the * before it transform it to a reference.
Sergey Alexandrovich Kryukov 13-Aug-13 19:40pm
   
Right...
—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900