简体   繁体   中英

Can I use the data() pointer from an std::array member variable before it is initialized? Gives warning

I am initializing a base class ( Matrix<3,3> in example) using the pointer to data stored in an std::array member of derived class SelfContained , being warned that " Field 'vdata' is uninitialized when used here " on doing so.

The warning makes sense, but I'm not sure the best way to avoid it... the code seems to work anyway, but I don't like to see warnings so I'm attempting to fix it.

I might be totally wrong, but it seems like std:array vdata is allocated when used here, so the data() pointer should already be valid even though the array is uninitialized. Since I'm initializing it immediately afterwards, I'm tempted to just ignore the warning... but I'm hopeful that there's a more "correct" way...

I've iterated through this a couple times... previously the std::array was a C-style array and grabbing the pointer to it did not cause a problem.

#include <iostream>
#include <array>

template<class ContainedType>
class SelfContained: public ContainedType{
public:
    typedef ContainedType Type;
    
    static constexpr size_t numel()     {   return Type::numel();}
    
    typedef std::array<double, numel()> DataArray;   //  Data Array Type
    DataArray vdata;
    
    SelfContained(DataArray arry_in): ContainedType(vdata.data()), vdata{arry_in} {} // WARNING: Field 'vdata' is uninitialized when used here
    
};


template <size_t m, size_t n>
class Matrix{
public:
    typedef double DoubleArray[n][m];   //  Data Array Type
    
    DoubleArray* pdata = NULL;        //  Pointer to data array: to be assigned on instantiation to let instance be a specified sub-array of something else.
    
    static constexpr size_t height() {return m;}
    static constexpr size_t width()  {return n;}
    static constexpr size_t numel()  {return m*n;}
    
    Matrix(double* p) noexcept : pdata{(DoubleArray*)p}{}

    // Element reference getters (mostly for internal convenience)
    template<typename ...Args>
    double& data(Args... vals){
        return get_data(*this, vals...);
    }
    template<typename ...Args>
    const double& data(Args... vals) const{
        return get_data(*this, vals...);
    }
    
    // Print Matrix
    void print() const {
        for (size_t j=0; j<height(); j++){
            for(size_t k=0;k<width(); k++){
                std::printf("%+15.7f ",data(j,k));
                //std::printf("%+4.1f ",data(j,k));
            } std::printf("\n");
        } std::printf("\n");
    }
    
private:
    
    // Helper functions for public element-reference getters ...
    // weird, but minimizes code dupication (const/non-const) by putting the guts here
    template<typename InstanceType>
    static auto get_data(InstanceType& instance, size_t row) -> decltype(instance.data(row)) {
        assert(row >= 0 && row < instance.numel());
        return (*(instance.pdata))[0][row];
    }
    template<typename InstanceType>
    static auto get_data(InstanceType& instance, size_t row, size_t col) -> decltype(instance.data(row,col)) {
        assert(col >= 0 && col < instance.width());
        assert(row >= 0 && row < instance.height());
        return (*(instance.pdata))[col][row];
    }
};

constexpr std::array<double,9> x0 = {1,0,0,0,2,0,0,0,3};

int main(int argc, const char * argv[]) {

    
    SelfContained<Matrix<3,3>>(x0).print();
    
    return 0;
}

Gives warning: Field 'vdata' is uninitialized when used here

Output:

     +1.0000000      +0.0000000      +0.0000000 
     +0.0000000      +2.0000000      +0.0000000 
     +0.0000000      +0.0000000      +3.0000000 

Any assistance is greatly appreciated. Thank you!

As far as I know, calling vdata.begin() before vdata is initialised is technically UB, even though the member function doesn't need to touch any members.

Another problem however is (DoubleArray*)p . Accessing though the reinterpreted pointer as if it points a DoubleArray has undefined behaviour when it doesn't actually point to such object.

The primary issue here is that you can't force members to be initialized before inherited values.

One possible solution is to move the data members that must be initialized first to another type, and inherit that first, which forces those inherited members to be initialized first. This way, vdata is initialized before the ContainedType constructor is called.

Doing so makes the "is this defined behavior" discussion pointless.

template<class ContainedType>
class SelfContainedMembers {
public:
    typedef ContainedType Type;

    typedef std::array<double, Type::numel()> DataArray;   //  Data Array Type
    DataArray vdata;

    SelfContainedMembers(DataArray arry_in): vdata{arry_in} { }
};

template<class ContainedType>
class SelfContained: private SelfContainedMembers<ContainedType>, public ContainedType {
public:
    typedef ContainedType Type;
    typedef SelfContainedMembers<ContainedType> Members;

    static constexpr size_t numel()     {   return Type::numel();}

    typedef typename Members::DataArray DataArray;

    SelfContained(DataArray arry_in): Members{arry_in}, ContainedType{Members::vdata.data()} {};
};

(See this example and note the lack of warnings.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM