Uninitialized std::complex constructor when using 'new'

旧街凉风 提交于 2019-12-08 18:51:04

问题


In profiling my program I realized that 10% of the code is spent in a stupid std::complex<double>() constructor, using new std::complex<double>[size_of_array].

I have searched through the web and the default constructor for std::complex seems to take as real and imaginary parts the values double(). Since C++ does not initialize double numbers, I wonder why g++ bothers to initialize std::complex with zeros, and whether I could work around this through the whole program in some way (*)

(*) right now I have to special case the functions that create arrays of complex numbers to allocate uninitialized arrays of doubles and recast them as complex.

Edit: as pointed below, it was an oversight on my side. The default constructor has empty constructors for the real and imaginary part (http://en.cppreference.com/w/cpp/numeric/complex/complex)

 complex( const T& re = T(), const T& im = T() );

but the specification then introduces special cases for double

 complex(double re = 0.0, double im = 0.0);

It is this special case that introduces all the overhead, as it bypasses the actual default constructor of 'double' which does nothing (same as for int, long, float, etc).


回答1:


I wonder why g++ bothers to initialize std::complex with zeros

Because the standard says it must do so, the default constructor is declared as:

constexpr complex(double re = 0.0, double im = 0.0);

so it sets both the members to zero.

It is normal for the standard library to safely initialize types, rather than leaving them uninitialized as you get with built-in types such as double and int*, for instance std::vector<double> zero-initializes its elements too if you resize it so that new elements get added. You can control this for vector by not adding elements to the vector until you know what values you want them to have.

One possible workaround for complex is to use a type that doesn't do the initialization:

struct D
{
  D() noexcept { }; // does not initialize val!
  D(double d) noexcept : val(d) { }
  operator double() const noexcept { return val; }
  D& operator=(double d) noexcept { val = d; return *this; }
  double val;
};

Now if you use std::complex<D> the default constructor does nothing. Add explicit to the converting constructor and/or the conversion operator to suit your taste.




回答2:


There is an easy way of doing it. If you "reserve" the memory with a std::vector its much faster because it doesn't call the constructor on each element.

ie this:

std::vector< std::complex< double > > vec;
vec.reserve( 256 );
for( int i = 0; i < 256; i++ )
{
    vec.push_back( std::complex< double >( 1, 1 ) );
}

will be significantly faster than this:

std::complex< double >* arr = new std::complex< double >[256];
for( int i = 0; i < 256; i++ )
{
    arr[i]( std::complex< double >( 1, 1 ) );
}
delete[] arr;

because the constructor is only called once in the first example.

It has the added advantage that you have RAII on your side and 'vec' will automatically be released when its out of scope.




回答3:


The following is from code I am developing.

Added later: As M.M. noted in the comments, the behavior is technically undefined. Now I see that if some sleazy weasel were to change the implementation of std::complex so that it could not be trivially constructed / destructed, this would come a cropper. See also the example at http://en.cppreference.com/w/cpp/types/aligned_storage.

#include <complex>
#include <type_traits>

typedef std::complex<double> complex;

// Static array of complex that does not initialize
// with zeros (or anything).

template<unsigned N>
struct carray {
    typedef
      std::aligned_storage<sizeof(complex), alignof(complex)>::type raw;

    raw val[N];

    complex& operator[] (unsigned idx) {
        return reinterpret_cast<complex&> (val[idx]);
    }

    complex* begin() { return &operator[](0); }
    complex* end() { return &operator[](N); }
};


来源:https://stackoverflow.com/questions/27290766/uninitialized-stdcomplex-constructor-when-using-new

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!