我可以在初始化之前使用 std::array 成员变量中的 data() 指针吗?发出警告

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

本文关键字:data 指针 警告 变量 初始化 成员 array std 我可以      更新时间:2023-10-16

我正在使用指向存储在派生类SelfContained的 std::array 成员中的数据的指针初始化一个基类(例如 Matrix<3,3>),并被警告">字段'vdata'在此处使用时未初始化"这样做。

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

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

我已经迭代了几次...以前 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"在此处使用时未初始化

输出:

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

任何帮助将不胜感激。谢谢!

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

然而,另一个问题是(DoubleArray*)p。通过重新解释的指针进行访问,就好像它指向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()} {};
};

(请参阅此示例并注意缺少警告。