Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在初始化std::array成员变量之前,是否可以使用该变量的data()指针?警告_C++ - Fatal编程技术网

C++ 在初始化std::array成员变量之前,是否可以使用该变量的data()指针?警告

C++ 在初始化std::array成员变量之前,是否可以使用该变量的data()指针?警告,c++,C++,在示例中,我使用指向派生类self-contained的std::array成员中存储的数据的指针初始化基类矩阵,并收到警告,在这里使用字段“vdata”时未初始化 这个警告是有道理的,但我不确定最好的避免方法。。。代码似乎无论如何都能工作,但我不喜欢看到警告,所以我正在尝试修复它 我可能完全错了,但在这里使用时似乎分配了std:array vdata,因此即使数组未初始化,数据指针也应该已经有效。由于我在之后立即初始化它,我很想忽略警告。。。但我希望有一个更正确的方法 我已经重复了好几次了。。

在示例中,我使用指向派生类self-contained的std::array成员中存储的数据的指针初始化基类矩阵,并收到警告,在这里使用字段“vdata”时未初始化

这个警告是有道理的,但我不确定最好的避免方法。。。代码似乎无论如何都能工作,但我不喜欢看到警告,所以我正在尝试修复它

我可能完全错了,但在这里使用时似乎分配了std:array vdata,因此即使数组未初始化,数据指针也应该已经有效。由于我在之后立即初始化它,我很想忽略警告。。。但我希望有一个更正确的方法

我已经重复了好几次了。。。以前,std::array是一个C样式的数组,获取指向它的指针不会导致问题

#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;
}
非常感谢您的帮助。谢谢大家!

据我所知,在初始化vdata之前调用vdata.begin在技术上是无效的,即使成员函数不需要接触任何成员

然而,另一个问题是DoubleArray*p。当DoubleArray实际上没有指向这样的对象时,通过重新解释的指针进行访问,就好像它指向DoubleArray一样,具有未定义的行为。

据我所知,在初始化vdata之前调用vdata.begin从技术上讲是错误的,即使成员函数不需要接触任何成员


然而,另一个问题是DoubleArray*p。当DoubleArray实际上没有指向这样的对象时,通过重新解释的指针进行访问,就像它指向DoubleArray一样,具有未定义的行为。

这里的主要问题是不能强制在继承值之前初始化成员

一种可能的解决方案是将必须首先初始化的数据成员移动到另一种类型,然后继承该类型,这将强制先初始化这些继承的成员。这样,vdata在调用ContainedType构造函数之前初始化

这样做会使定义的行为讨论变得毫无意义

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()} {};
};

请参阅并注意缺少警告。

这里的主要问题是不能强制在继承值之前初始化成员

一种可能的解决方案是将必须首先初始化的数据成员移动到另一种类型,然后继承该类型,这将强制先初始化这些继承的成员。这样,vdata在调用ContainedType构造函数之前初始化

这样做会使定义的行为讨论变得毫无意义

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()} {};
};

请参阅并注意缺少警告。

您的编译器/版本是什么?使用-Wall的最新gcc没有问题。如果需要指向底层数组结构的指针,则应使用数据而不是begin。begin返回一个迭代器,它不一定是指针,这在c++20中可能会改变。数据总是返回指针,并且比开始更好地传达意图。读取未初始化的数据是未定义的行为。另外,begin返回一个迭代器,而不是指针。由于初始化顺序的原因,您会得到警告,因为包含类型的基类在vdata之前初始化。在这种情况下,您可以将arry_传递给ContainedType构造函数,而不是vdata。@JesperJuhl我的评论只说明了它所说的,gcc不显示OP的行为。它并没有说gcc是正确的。它说OP的编译器和最新的gcc之间存在不一致性,在许多情况下,了解这一点至少在一定程度上是有用的。你的编译器/版本是什么?使用-Wall的最新gcc没有问题。如果需要指向底层数组结构的指针,则应使用数据而不是begin。begin返回一个迭代器,它不一定是指针,这在c++20中可能会改变。数据总是返回指针,并且比开始更好地传达意图。读取未初始化的数据是未定义的行为。另外,begin返回一个迭代器,而不是指针。由于初始化顺序的原因,您会得到警告,因为包含类型的基类在vdata之前初始化。在这种情况下,您可以将arry_传递给ContainedType构造函数,而不是vdata。@JesperJuhl我的评论只说明了它所说的,gcc不显示OP的行为。它并没有说gcc是正确的。它说OP的编译器和最新的gcc之间存在不一致性,在许多情况下,了解这一点至少在一定程度上是有用的。在不读取容器状态的情况下,如何开始逃之夭夭?它不能,所以它一定是UB。@NathanOliver,Monica,因为它根本不需要知道任何州的情况。这完全取决于当时已知的成员地址。但据我所知,成员函数调用是UB。你能推荐一个更好的做法吗?我的印象是,由于数组成员存储在双[9]*和双[3][3]*之间,通过双[3]*连续施放
*因为在某些情况下,我只想访问数组的一部分就可以了。。。另一个情况是,它似乎在工作,但是我不经常用C++来写,当我在我不应该做的事情上消失的时候。还有,有一个方法可以在不使用成员函数的情况下得到那个地址,因为地址是已知的,但是成员函数导致UB?@杰瑞米,存储是扁平的。只需将其用作平面数组,而不是试图将其转换为多维数组。如果不读取容器的状态,如何开始逃离?它不能,所以它一定是UB。@NathanOliver,Monica,因为它根本不需要知道任何州的情况。这完全取决于当时已知的成员地址。但据我所知,成员函数调用是UB。你能推荐一个更好的做法吗?我的印象是,由于数组成员是通过double*连续存储在double[9]*和double[3][3]*之间,因为在某些情况下,我只想访问数组的一部分就可以了。。。另一个情况是,它似乎在工作,但是我不经常用C++来写,当我在我不应该做的事情上消失的时候。还有,有一个方法可以在不使用成员函数的情况下得到那个地址,因为地址是已知的,但是成员函数导致UB?@杰瑞米,存储是扁平的。只需将其用作平面阵列,而不是试图将其转换为多维阵列。这看起来很有希望。。。非常感谢。在某种程度上,我养成了一种避免像瘟疫那样的多重遗传的习惯,我想是时候克服它了,并学会如何在不射中自己脚的情况下做到这一点。再次感谢!这看起来很有希望。。。非常感谢。在某种程度上,我养成了一种避免像瘟疫那样的多重遗传的习惯,我想是时候克服它了,并学会如何在不射中自己脚的情况下做到这一点。再次感谢!