C++ 如何测试是否定义了结构的成员(c+;+;)

C++ 如何测试是否定义了结构的成员(c+;+;),c++,C++,我试图测试在调用函数之前是否定义了声明为指向void的指针的结构的成员。然而,似乎每当我向裸体程序添加任何额外的内容时(例如末尾的std::cout),“testB”总是等于true。我无法更改“struct a”的声明方式,因为它是外部库的一部分 那么,如何测试声明为空指针的结构的成员实际上是用正确的类型定义的呢 示例结构 我正在尝试测试的示例代码 失败的示例测试程序 #包括 int main(){ 结构a aa; 结构b-bb; //下面这行被注释掉了,所以我希望'testB'的值为fals

我试图测试在调用函数之前是否定义了声明为指向void的指针的结构的成员。然而,似乎每当我向裸体程序添加任何额外的内容时(例如末尾的std::cout),“testB”总是等于true。我无法更改“struct a”的声明方式,因为它是外部库的一部分

那么,如何测试声明为空指针的结构的成员实际上是用正确的类型定义的呢

示例结构 我正在尝试测试的示例代码 失败的示例测试程序
#包括
int main(){
结构a aa;
结构b-bb;
//下面这行被注释掉了,所以我希望'testB'的值为false
//aa.b=&bb;
结构b*testB=静态转换(aa.b);
if(testB){

这是一个有趣的问题,因为你要问两个问题:

  • 某些类型的
    T
    是否有名为
    b
    的成员
  • 如果上一个为真,则该成员是否为类型
    void*
我们可以在编译时完成这一切

在您的解决方案中,您不应该尝试执行静态强制转换,因为您有违反别名规则的风险,并且您还假设静态成员首先出现

我们可以使用一个简单的检测习惯用法来检测条件是否满足:

template<class T, class=void>
struct has_void_ptr_member_named_b_t : std::false_type{};

template<class T>
struct has_void_ptr_member_named_b_t<T, std::void_t<decltype(std::declval<T>().b)>> : 
    std::is_same<std::decay_t<decltype(std::declval<T>().b)>, void*>
{};
template<class T>
constexpr bool has_void_ptr_member_named_b_v = has_void_ptr_member_named_b_t<T>::value;
这使用C++17作为它的
std:void\u t
,这很容易自己创建。我认为,如果你足够努力,你可以在C++11中使用所有这些。如果你想使用一个预先制作的检测习惯用法(目前是非标准的),你也可以研究一下


正如@YSC在下面的评论中指出的,也许您只是想测试
aa.b
是否为非空

您应该将其初始化为
nullptr
,因为作为基本类型(指针),它没有默认构造函数,并且保持未初始化状态:

struct a {
   void *b = nullptr;
};
您仍然应该避免强制转换,因为您会面临未定义行为的风险,并且您假设结构总是对齐的

a aa;
b bb;
auto test = [&aa](){
    if (aa.b)
        std::cout << "aa.b is defined\n";
    else
        std::cout << "aa.b is null\n";
};
test(); // "aa.b is null"
aa.b = &bb;
test(); // "aa.b is defined"
aa;
b-bb;
自动测试=[&aa](){
如果(aa.b)

std::cout在尝试使用其值之前,您需要确保
aa.b
已初始化。确保这一点的最简单方法是更改
a
,以便它在构造函数中初始化

这里有一些方法可以做到这一点

struct a
{
    void* b = nullptr; // data member with initialiser
}

struct a
{
    void* b;
    a() : b(nullptr) {} // user defined default constructor
}

struct a
{
    void* b;
    a(void* _b = nullptr) : b(_b) {} // user defined constructor with default parameter
}

如果您不能修改,您必须亲自采取措施确保它已初始化。可能是包装器(适配器)对象可以用作a的更好的实现。在这里,它是在类主体中默认初始化的,因此您可以确保指针在未初始化时将为null。您可以在所有代码中使用下面的类型安全包装器来代替“struct a”,然后在需要与定义它的库交互时获取_a()。(顺便说一句,界面非常糟糕,但你被卡住了。)

我不是在处理b*指向的对象的内存管理,这是您的问题。:)


如果您有一个void*,并且它被初始化为null ptr,您可以判断它是否为null,但无法确定它是否“正确”初始化为指向正确的类型。这完全是该接口的作者强加给您的问题。如果在声明指针时未初始化指针,则以后无法确定它是否包含有效地址或垃圾,因此您希望始终对其进行初始化,并且无法确定它指向的对象的类型。因此请小心并使用包装器来帮助防止对其进行非类型化访问。

使用测试框架。我看不到您在测试代码中初始化
aa.b
。读取未初始化的值是未定义的行为。是否查看对象
aa
是否已声明(定义)名为
b
的成员变量是否已将成员变量
aa.b
初始化为非空值?我的评论是——不初始化
aa.b
,然后尝试测试它会触发未定义的行为。除此之外,每次尝试都会产生不同的结果。如果无法更改
a
没有办法检查变量是否初始化:确保在初始化变量之前没有人访问它是开发人员的工作。Andy,我不确定这是否回答了这个问题。OP更感兴趣的似乎是运行时信息,是(
void*
)指向有效对象的指针?(我不确定,OP不清楚)@YSC:然后我误读了OPs的意图:-(在我看来,他们后来的句子表明他们只是想知道该成员是否作为空指针存在。你为什么要使用连词?(True和X)与(X)相同。它看起来更复杂。另外,如果已经需要c++17,为什么要定义自己的void\u t?@ChrisUzdavinis:谢谢你指出了所有这些。在“成功”之后停止的混合方式考虑到C++14的答案与C++17的答案,我最近因为太复杂而被拒绝了代码审查,所以我很敏感地试图让这些东西尽可能容易阅读(可以这么说),这很容易理解。:)非常感谢你的回答。我猜你的意思是?SafeA(b*bPtr):aa{bPtr}是的,谢谢。我会纠正的。
struct b { };
struct a {
   void *b;
};
// ...

std::cout << std::boolalpha << has_void_ptr_member_named_b_v<a> << std::endl; // true
std::cout << std::boolalpha << has_void_ptr_member_named_b_v<b> << std::endl; // false
struct a {
   void *b = nullptr;
};
a aa;
b bb;
auto test = [&aa](){
    if (aa.b)
        std::cout << "aa.b is defined\n";
    else
        std::cout << "aa.b is null\n";
};
test(); // "aa.b is null"
aa.b = &bb;
test(); // "aa.b is defined"
struct a
{
    void* b = nullptr; // data member with initialiser
}

struct a
{
    void* b;
    a() : b(nullptr) {} // user defined default constructor
}

struct a
{
    void* b;
    a(void* _b = nullptr) : b(_b) {} // user defined constructor with default parameter
}
class SafeA {
private: 
    a aa{}; // will initialize internal pointer in aa to nullptr

public:
    SafeA() = default;
    SafeA(SafeA const &) = default;
    SafeA(b * bPtr) : aa{bPtr} { }
    SafeA& operator=(SafeA const&) = default;
    SafeA& operator=(b *ptr) {
       aa.b = ptr;
       return *this;
    }       
    a& get_a() { return aa; }
    a const& get_a() const { return aa; }
};