我正在尝试测试在调用函数之前是否定义了声明为指向 void 的指针的结构成员。但是,似乎每当我向基本程序添加任何额外内容(例如末尾的 std::cout)时,“testB”总是等于 true。我无法更改“struct a”的声明方式,因为它是外部库的一部分。
那么,我该如何测试声明为 void 指针的结构成员实际上是否使用正确的类型定义?
示例结构
struct b { };
struct a {
void *b;
};
我正在尝试测试的示例代码
struct a myA;
struct b myB;
myA.b = &myB;
foo(myA);
失败的示例测试程序
#include <iostream>
int main() {
struct a aa;
struct b bb;
// the following line is commented out so I'm expecting 'testB' to evaluate to false
// aa.b = &bb;
struct b *testB = static_cast<struct b*>(aa.b);
if(testB) {
std::cout << "is defined" << std::endl;
} else {
std::cout << "is not defined" << std::endl;
}
std::cout << "why does adding this line out result in testB equating to true" << std::endl;
return 0;
}
这是一个有趣的问题,因为您在问两个问题:
- 是否输入
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;
现在你可以问has_void_ptr_member_named_b_v
可以转换为 bool
,对于任何给定类型 T
例如:
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
它的 std:void_t
使用 C++17 ,这很容易自己创建。我认为,如果你足够努力,你可以在 C++11 中实现所有这些。您还可以查看 std::experimental::is_detected如果您想使用预制的检测惯用语(目前是非标准的)。
正如@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"