4

I simplified my code down to the root of the trouble:

//==============================================================================
// PRE-DEFINITIONS

#define GIVE_ME_ODD_BEHAVIOUR true

//==============================================================================
// INCLUDES

#include <iostream>

//==============================================================================
// TYPES

//------------------------------------------------------------------------------
template<typename T> struct X {

  T data;

  X() : data(0)
  { std::cout << "X construction @ " << this     << std::endl; }

  template<typename TT>
  X(const X<TT> & other) : data(other.data)
  { std::cout << "X construction @ " << this << " from " << &other << std::endl; }

  ~X()
  { std::cout << "X destruction  @ " << this << std::endl; }

  template<typename TT>
  void Copy(const X<TT> & other)
  { std::cout << "X copy         @ " << this << " from " << &other << std::endl; }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
template<typename T>
X<double> XConversion(const X<T> & other)
{

#if GIVE_ME_ODD_BEHAVIOUR

  return X<double>(other);

#else

  X<double> d;
  d.Copy(other);

  return d;

#endif

}


//==============================================================================
// MAIN

int main()
{

  X<double> d;
  X<int>    i;

  std::cout << std::endl;

  d = XConversion(i);

  std::cout << std::endl;

  d = XConversion(d);         // !!!

  std::cout << std::endl;

  return 0;

}

which, with

GIVE_ME_ODD_BEHAVIOUR true

gives the output:

X construction @ 0x23aa70
X construction @ 0x23aa60

X construction @ 0x23aa80 from 0x23aa60
X destruction  @ 0x23aa80

X destruction  @ 0x23aa90 // never created !!!

X destruction  @ 0x23aa60
X destruction  @ 0x23aa70

It seems similar to THIS problem but i do have the ctor defined. I see the copy elision optimisation point here but:

  • why the copiler destructs the object that it optimised out and thus never created?
  • how can I ensure that it does not happen ?

Additional info:

I tried gcc 4.8.1 and C++ Builder XE3, optimisation off, debug compilation, both with the same result.

I tried to remove the template-ness like

struct X {

  double data
  ...
  X(const X &)
  ...

};

but with the same result.

One way to solve this would be, e.g., to make the cctor private and to public the Copy method only. But this would prevent me (anybody) from even returning the object from a function...


Background:

I have a set of template classes that are mutually friends and convertible. The classes share the same data but manipulate them in different ways via partial specialisation. The instances may either do soft or raw copy of the data as needed. If they do the soft copy, ref counter is increased. With the extra deletion of the instance that was actually never created, the ref counter is decreased without its prior incrementation.

Community
  • 1
  • 1
PavDub
  • 71
  • 4

1 Answers1

4

Seems the compiler generates a copy constructor for your case, maybe it does not like the one with the templates (if I recall correctly copy constructors are not (or may not be) template based ... might be wrong here though)... see: Copy constructor of template class

Adding the following code:

X(const X & other) : data(other.data)
{ std::cout << "X Copy construction @ " << this << " from " << &other << " as " << typeid(X).name() << std::endl; }


template<typename TT>
X(const X<TT> & other) : data(other.data)
{ std::cout << "X construction @ " << this << " from " << &other << " as " << typeid(TT).name() << std::endl; }

gives the following output:

X construction @ 0x7fff3496c040
X construction @ 0x7fff3496c038

X construction @ 0x7fff3496c020 from 0x7fff3496c038 as i
X destruction  @ 0x7fff3496c020

X Copy construction @ 0x7fff3496c018 from 0x7fff3496c040 as 1XIdE
X destruction  @ 0x7fff3496c018

X destruction  @ 0x7fff3496c038
X destruction  @ 0x7fff3496c040

so there is your missing object. (tested both with clang and g++, same behaviour)

Community
  • 1
  • 1
Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 1
    Just to confirm: a templated constructor is _not_ considered a copy constructor, in the sense that it won't prevent the compiler from generating one. (On the other hand, when choosing the constructor to be called, the compiler _will_ consider the template instantiations, even in contexts where you are copying. If, for example, his non-template copy constructor took a non-const reference, and he was copying a temporary, the template instantiation would be choosen.) – James Kanze Jul 15 '14 at 13:19