Help with template function parameters

Started by
39 comments, last by null_pointer 21 years, 7 months ago
I cannot figure out why the code at the end of this source listing does not compile when I call it with a literal. Can anyone help? And if so, could we please proceed from the premise that null knows what he is doing so that we can actually give definitive answers to the problem he is asking about? Much obliged; no grudges.
  
# include <new>
# include <stdexcept>

using std::logic_error;

// construct - invokes constructor, optionally passes arguments


template <typename T>
  inline T* construct (void* address)
    { return new (address) T; }

template <typename T, typename P1>
  inline T* construct (void* address, P1& p1)
    { return new (address) T (p1); }

template <typename T, typename P1, typename P2>
  inline T* construct (void* address, P1& p1, P2& p2)
    { return new (address) T (p1, p2); }

// destruct - invokes destructor


template <typename T>
  inline void* destruct (T* object)
    { delete object; return object; }

// reconstruct - invokes destructor and then constructor in-place


template <typename T>
  inline T* reconstruct (T* object)
    { return construct (destruct (object)); }

template <typename T, typename P1>
  inline T* reconstruct (T* object, P1& p1)
    { return construct (destruct (object), p1); }

template <typename T, typename P1, typename P2>
  inline T* reconstruct (T* object, P1& p1, P2& p2)
    { return construct (destruct (object), p1, p2); }

// dynamic - a class that adds dynamic construction to its T


template <typename T> class dynamic
{
  char memory [sizeof (T)]; // TODO: fix memory alignment issues

  T*   object;              // TODO: implement copying


  void verify () { if (not object) throw logic_error (""); }
  void verify_null () { if (object) throw logic_error (""); }

public:
   dynamic () : object (0) {}
  ~dynamic ()              { if (object) ::destruct (object); }

  T* operator -> () { verify (); return  object; }
  T& operator  * () { verify (); return *object; }

  // construct


    void construct ()
      { verify_null (); object = ::construct <T> (memory); }

  template <typename P1>
    void construct (P1& p1)
      { verify_null (); object = ::construct <T> (memory, p1); }

  template <typename P1, typename P2>
    void construct (P1& p1, P2& p2)
      { verify_null (); object = ::construct <T> (memory, p1, p2); }

  // destruct


    void destruct ()
      { verify (); ::destruct (object); object = 0; }

  // reconstruct


    void reconstruct ()
      { destruct (); construct (); }

  template <typename P1>
    void reconstruct (P1& p1)
      { destruct (); construct (p1); }

  template <typename P1, typename P2>
    void reconstruct (P1& p1, P2& p2)
      { destruct (); construct (p1, p2); }
};

// example code illustrating problem


struct myclass { myclass (int n) {} };

const int five = 5;

int main ()
{
  dynamic <myclass> dm;

  dm.construct (five);
  //dm.construct (5) // <- this one wouldn''t work!!

}  
Advertisement
Well, at a first glance i would say it was because you are expecting non-const references to your construct() member functions. But then again "five" is const, and therefore shouldn't work either. Whats the error message?

quote:
And if so, could we please proceed from the premise that null knows what he is doing so that we can actually give definitive answers to the problem he is asking about? Much obliged; no grudges.


Wow, attitude problem.


[edit]

Ok, i put your code into my compiler, and changed all references going into construct() members and global functions const. It compiled fine when using a literal.

[edited by - sark on September 11, 2002 8:21:28 PM]
quote:Original post by sark
Well, at a first glance i would say it was because you are expecting non-const references to your construct() member functions.

Passing a const value to a function expecting a non-const value is never a problem. Doing the inverse is.

I would suspect type ambiguity - is "5" a char, a float, an int, a double...? That''s why five works (it has a known type), but the literal doesn''t (it''s type cannot unambiguously be inferred). Try placing a type cast before it:
dm.construct((int)5); 
It won''t work for 5 for the same reason this doesn''t compile:
const int &i = &5;

Try changing construct to use value semantics (lose the references).


Also, gcc fails to compile it even for the const int five;

The :: scope operator fails to make it use the global functions, and it attempts to use one of the construct template methods.

Placing the global construct functions in a namespace, and referring to that explicit name space inside the dynamic class resolved this. I''m inclined to think that''s a bug in gcc 2.95.3
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
quote:Passing a const value to a function expecting a non-const value is never a problem. Doing the inverse is.

This is true for values, in both directions (copy constructors and assignment operators take const references; const references can be bound to both const and non-const objects), but in this case references are being created, and it''s not true for references.

Thus, whilst one can say:

  const int i = 0;int j = i;const int k = j;  

without issue, one cannot say:

  const int i = 0;int& j = i;  

although

  int j = 0;const int& k = j;  

is fine.

quote:I would suspect type ambiguity - is "5" a char, a float, an int, a double...?

An int. It''s well-defined:

The type of an integer literal depends on its form, value, and suffix. If it is decimal and has no suffix, it has
the first of these types in which its value can be represented: int, long int; if the value cannot be represented
as a long int, the behavior is undefined. If it is octal or hexadecimal and has no suffix, it has the
first of these types in which its value can be represented: int, unsigned int, long int, unsigned
long int. If it is suffixed by u or U, its type is the first of these types in which its value can be represented:
unsigned int, unsigned long int. If it is suffixed by l or L, its type is the first of these
types in which its value can be represented: long int, unsigned long int. If it is suffixed by ul,
lu, uL, Lu, Ul, lU, UL, or LU, its type is unsigned long int.


quote:That''s why five works (it has a known type), but the literal doesn''t (it''s type cannot unambiguously be inferred).

Yes it can.

char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;}  /*** drpizza@battleaxe.net ***/

sark was perfectly right, with both her diagnosis of the problem in the code and with null's attitude problem, especially as null was extremely arrogant measured by her knowledge of the C++ const semantics.

But forgive me, Oluseyi and Magmai, your answers sure point out that your understanding of the topic is even inferior tho null's, thereby almost sanctioning her prematurely arrogant statement.

Oluseyi said, passing non-const when const was expected wouldn't normally impose a problem, and she was right. But then she said this exact thing was happening here, and that was utterly wrong. An implicitly const reference was about to be passed when a non-const reference was expected, and that means passing const when non-const is expected and must not compile.

Magmai then wittily responded that leaving out the references might make everything plusgood. Dear Magmai, imagine T's copy construction was costly. In this case the chance to do it only once instead of twice would be lost as soon as pass-by-reference was used. And even worse, imagine P1 was a type different from T, and P1 didn't provide copy semantics at all. Then construction from P1 using the construct method would be prohibited, although T's semantics supported it!
I honestly hope you're not working as a professional C++ programmer, for your sake and you employer's as well.

Finally, dear null, you're messing with template members without having understood the basic semantics of the language you are using, int this case the rules of pass-by-reference. And still you dare be as arrogant? Shame, I say, shame on you.

Hopeful to have beaten you, dear null, in terms of arrogance,

sincerely yours,

shadi


P.S. sark, you're the good one.

[edited by - shadi on September 11, 2002 9:28:31 PM]
It doesn''t compile because you are missing a semi colon?

//dm.construct (5) // <- this one wouldn''t work!!


Joke aside. But even VC++ 6 compiles it fine. Did you edit the post or something? But I getting heap assertions failures when executing.
quote:Wow, attitude problem.


I waded through 150 posts of anti-American crud on another site supposedly dedicating a thread to remembering those who died last year in the terrorist attack, then I checked my other thread (which is still just a bunch of "are you sure you want to do this" posts), and then I created this topic. No one here seems to understand the problem, but one of DrPizza''s comments made me realize why my code does not work as I intended.


quote:Ok, i put your code into my compiler, and changed all references going into construct() members and global functions const. It compiled fine when using a literal.


That''s fine, but I want the function to work with const and non-const literals and references. Which, I realize, is a rather tall order.


quote:Passing a const value to a function expecting a non-const value is never a problem. Doing the inverse is.


The function templates are not even taking arguments by value.


quote:It won''t work for 5 for the same reason this doesn''t compile:
const int &i = &5;

Try changing construct to use value semantics (lose the references).


Oh, now I''m *supposed* to pass by value.


quote:An int.


So that is the problem! Any idea why it is not *const* int? That would solve my problem. Of course, I could try providing overloads for all of the intrinsic types, but I think that would require a lot of redundant code.


quote:sark was perfectly right, with both her diagnosis of the problem in the code and with null''s attitude problem, especially as null was extremely arrogant measured by her knowledge of the C++ const semantics.


Rendering judgments from on high now, is it?

The compiler is supposed to detect whether the typename P1 is "int" or "const int." That is why passing a named constant works fine but not a literal. A literal''s type is "int", not "const int", so it P1& gets detected as "int&" to which we obviously cannot assign a literal. sark post did not provide any information that I did not already know, nor did you.

In the future if you desire an explanation, please refrain from narrating the thread.


quote:But forgive me, Oluseyi and Magmai, your answers sure point out that your understanding of the topic is even inferior tho null''s, thereby almost sanctioning her prematurely arrogant statement.


(Why is everyone a "she" all of a sudden?)


quote:Finally, dear null, you''re messing with template members without having understood the basic semantics of the language you are using, int this case the rules of pass-by-reference. And still you dare be as arrogant? Shame, I say, shame on you.


...I thought the Marx brothers were dead.


quote:Hopeful to have beaten you, dear null, in terms of arrogance,


I agree completely. All hail the King of Pointless Titles!
quote:It doesn''t compile because you are missing a semi colon?


Alright, alright.


quote:But even VC++ 6 compiles it fine.


You need to uncomment the line that reads:

//dm.construct (5) // <- this one wouldn''t work!! 


and then comment out the other call to construct.


quote:But I getting heap assertions failures when executing.


AFAIK you should not, unless perhaps there is some funky alignment issue with that char array. Or you could be leaving both constructor calls in there, which might result in some debug assertions.
quote:Original post by null_pointer
I cannot figure out why the code at the end of this source listing does not compile when I call it with a literal.

The hidden irony of this is that I saw your commented line in the other thread and I nearly posted to mention why it wouldn't compile. Then I remembered the amount of whining I've had directed at me in these forums for posting about something off-thread and decided not to (not from you, null_pointer). This forum in particular is getting to the point where its really hard to post much at all without getting heavily criticised - no matter whether you are asking a question or trying to help, or even just trying to have a casual discussion, someone always interprets you as doing so in "the wrong way". It makes me wonder if its really worth the bother.

[edited by - SabreMan on September 12, 2002 4:46:28 AM]

This topic is closed to new replies.

Advertisement