指向成员的指针:在GCC中工作,但在VS2015中不工作

Pointer to member: works in GCC but not in VS2015

本文关键字:工作 但在 VS2015 GCC 成员 指针      更新时间:2023-10-16

我正在尝试实现一个"属性"系统,将c++实例转换为JSON,反之亦然。我从Guillaume Racicot对这个问题的回答中提取了一部分代码(c++ JSON序列化)并简化了它。

我是这样做的。我有一个Property类:

template <typename Class, typename T>
struct Property {
    constexpr Property(T Class::* member, const char* name) : m_member(member), m_name(name) {}
    T Class::* m_member;
    const char* m_name;
};

m_member指向Class的一个特定成员

假设我想为User类定义属性,我希望能够像这样进行,能够为成员分配一个属性名称:

class User
{
public:
    int age;
    constexpr static auto properties = std::make_tuple(
        Property<User, int>(&User::age, "age")
    );
}

此代码在GCC(http://coliru.stacked-crooked.com/a/276ac099068579fd)中编译和工作正确,但在Visual Studio 2015 Update 3中不正确。我得到这些错误:

main.cpp(19) : error C2327 : 'User::age' : is not a type name, static, or enumerator
main.cpp(19) : error C2065 : 'age' : undeclared identifier
main.cpp(20) : error C2672 : 'std::make_tuple' : no matching overloaded function found
main.cpp(20) : error C2119 : 'properties' : the type for 'auto' cannot be deduced from an empty initializer

是否有一个解决方案,使其在Visual Studio 2015 Update 3中工作?

我的首选解决方案是将properties成员数据替换为properties成员函数:

class User
{
public:
    int age;
    constexpr static auto properties() { return std::make_tuple(
        Property<User, int>(&User::age, "age")
    ); }
};

之所以有效,是因为在成员函数的定义中,类被认为是完全定义的。它还具有理想的属性,即如果使用odr,则不需要单独定义properties

当MSVC想要计算properties的类型时,它不知道User有一个成员age

我们可以解决这个问题。

template<class T>struct tag_t{constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag{};
template<class T>
using properties = decltype( get_properties( tag<T> ) );
class User
{
public:
  int age;
};
constexpr auto get_properties(tag_t<User>) {
  return std::make_tuple(
    Property<User, int>(&User::age, "age")
  );
}

在JSON反射代码中,只需将std::decay_t<T>::properties替换为get_properties( tag<std::decay_t<T>> )

这有几个优点。首先,可以对一些不属于自己或希望无缝地使用属性修改的类进行改造。通过仔细使用名称空间和ADL启用调用点,您甚至可以对std中的(某些)类型这样做(仅具有public成员;至少配对)。

第二,它避免了属性上可能的odr使用需求。属性现在是一个constexpr返回值,而不是一些可能需要存储的全局数据。

第三,它允许像上面那样在类定义外编写属性,或者在类内作为friend内联编写属性,以获得最大的灵活性。

如果绝对需要在User类中定义属性,也许您可以使用helper模板化的constexpr函数,如:

#include <tuple>
template <typename Class, typename T>
struct Property {
    constexpr Property(T Class::* const member) : m_member{ member } {}
    T Class::* const m_member;
};
template <class T, class V>
constexpr Property<T, V> get_age_property() {
    return  Property<T, V>(&T::age);
}
class User
{
public:
    int age;
    constexpr static std::tuple<Property<User, int>> properties = std::make_tuple(
         get_age_property<User, int>()
    );
};
int main()
{
}

它似乎可以在webcompiler中编译,即vc++ 19.00.23720.0

相关文章: