下面的代码给出了以下编译错误:
main.cpp:5:48: error: invalid use of non-static data member 'id'
static constexpr int Type::* mem_ptr_id = &id;
^~
main.cpp:5:34: error: default initialization of an object of const type 'int Type::*const'
static constexpr int Type::* mem_ptr_id = &id;
^
= nullptr
main.cpp:6:46: error: invalid use of non-static data member 'id'
static constexpr auto mem_ptr_id_auto = &id;
^~
main.cpp:6:27: error: declaration of variable 'mem_ptr_id_auto' with deduced type 'const auto' requires an initializer
static constexpr auto mem_ptr_id_auto = &id;
^
4 errors generated.
这是意料之中的
#include <iostream>
struct Type {
int id;
static constexpr int Type::* mem_ptr_id = &id; // &Type::id required
static constexpr auto mem_ptr_id_auto = &id; // &Type::id required
};
int main() {
Type test;
test.*Type::mem_ptr_id = 5; // same as test.id = 5
test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
std::cout << test.id << std::endl; // expected: 5
return 0;
}
我需要一种方法来拥有我的类/结构的静态指针到成员变量,而不显式地命名类型(类/结构)名称. 有什么建议吗?
注意:为了避免auto
变成int&
,我使用了指针到成员的包装器:
template<typename T, class E>
struct Pointer2Member {
using var_t = T;
using entity_t = E;
using mem_ptr_t = T E::*;
T E::* data;
constexpr Pointer2Member() {}
constexpr Pointer2Member(T E::* val) : data(val) {}
constexpr operator T E::* () {
return data;
}
};
template<auto ptr>
struct Pointer2MemberOf;
template<typename T, class E, T E::* ptr>
struct Pointer2MemberOf<ptr> : Pointer2Member<T, E> {
constexpr Pointer2MemberOf() : Pointer2Member<T, E>(ptr) {}
constexpr operator Pointer2Member<T, E>() {
return *this;
}
};
struct Type {
int id;
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>(); // Pointer2MemberOf<&Type::id>() required
};
,但它给出相同的错误:
main.cpp:34:58: error: invalid use of non-static data member 'id'
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
^~
main.cpp:34:27: error: declaration of variable 'mem_ptr_id' with deduced type 'const auto' requires an initializer
static constexpr auto mem_ptr_id = Pointer2MemberOf<&id>();
^
main.cpp:40:17: error: no member named 'mem_ptr_id_auto' in 'Type'
test.*Type::mem_ptr_id_auto = 5; // same as test.id = 5
~~~~~~^
3 errors generated.
注释2:后续注释"What do you need this for":
不幸的是,这是一个非常复杂的可变模板解决方案,我不能多说。
我想实现的是创建一个模板类Result<...>
,它可以存储不同类的自定义成员变量。
Result<User::id, User::username, Post::id>
和Result<User, Post::id>
应该都是可行的语法,应该有Result<User, Post::id>::get<PROPERTY>
,应该能够有Result<User, Post::id>::get<User>
和Result<User, Post::id>::get<User::id>
(是的,User
,而不是Post
)。
想象一个库中的Result<...>
类将被初学者c++程序员使用,所以我不想使用&User::id
语法,因为它可能对他们来说太复杂而难以理解。
#define Member(TYPE, NAME) TYPE _##NAME; static constexpr auto NAME = Pointer2Member(&_##NAME)
struct User {
Member(int, id);
Member(std::string, username);
};
Result<User::id> result1;
result1.get<User::id>() = 5;
Result<User> result2;
result2.get<User::id>() = 6;
result2.get<User::username>() = "John";
可以,但不是静态的。一般来说,你可以用std::remove_reference_t<decltype(*this)>
替换对类/结构名的引用,比如Test
,但前提是this
可用。
例如,你可以用decltype(&std::remove_reference_t<decltype(*this)>::somefunc)
代替decltype(&Test::somefunc)
,这在模板中很有用。
如果你把你的例子修改成这样,那么它也可以工作:
#include <iostream>
struct Type {
int id;
int Type::* mem_ptr_id = &std::remove_reference_t<decltype(*this)>::id; // same as &Type::id
};
int main() {
Type test;
test.*test.mem_ptr_id = 5;
int Type::* mem_ptr_id_local = &std::remove_reference_t<decltype(test)>::id;
test.*mem_ptr_id_local = 5;
auto mem_ptr_id_auto = &std::remove_reference_t<decltype(test)>::id;
test.*mem_ptr_id_auto = 5;
std::cout << test.id << std::endl; // expected: 5
return 0;
}
正如您所看到的,这些不再是静态的(mem_ptr_id具有默认初始化),并且访问mem_ptr_id
有点尴尬。但除此之外,只要this
可用,它就可以以这种方式实现,并且它对于模板/宏非常有用。最低c++ 14要求。
也只是为了好玩,我将粘贴一个示例代码片段,它可以在实践中有用:
template <typename T> struct getvfunc_wrapper_autotype;
template<typename T, typename RT, typename... Args>
struct getvfunc_wrapper_autotype<RT(T::*)(Args...)>
{
typedef RT(__fastcall* FT)(PVOID, Args...);
inline static FT get(const void* inst, size_t index, size_t offset = 0)
{
return reinterpret_cast<FT>(getvtable(inst, offset)[index]);
}
};
#define DECLARE_VFUNC(index, return_type, fname, arg_types, arg_invokation)
inline return_type fname arg_types
{
auto* _ = this;
return getvfunc_wrapper_autotype< decltype(&std::remove_reference_t<decltype(*this)>:: fname ) >::get(_, index) arg_invokation;
}
class IVEngineClient
{
public:
void* m_vtable;
DECLARE_VFUNC(18, void, GetScreenSize, (int& wide, int& tall), (_, wide, tall)
DECLARE_VFUNC(21, void, ClientCmd, (const char* szCmdString), (_, szCmdString))
};
正如我们所看到的,在上面的宏中,每次都必须手动传递IVEngineClient
作为参数之一,这将是非常烦人的。这个技巧可以很好地避免这种情况。