Pointer to member: works in GCC but not in VS2015

前端 未结 3 1599
难免孤独
难免孤独 2021-02-13 10:54

I\'m trying to implement a \"property\" system to convert C++ instances into JSON and vice versa. I took a part of the code from Guillaume Racicot\'s answer in this question (C+

相关标签:
3条回答
  • 2021-02-13 11:24

    My preferred workaround is just to replace the properties member data with a properties member function:

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

    This works because in the definition of a member function, the class is considered to be completely defined. It also has the desirable attribute that properties doesn't need to be separately defined if odr-used.

    0 讨论(0)
  • 2021-02-13 11:32

    If it is absolutely needed to properties be defined in User class maybe you could make use of helper templated constexpr function like:

    #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()
    {
    }
    

    It seems to compile in webcompiler i.e. VC++ 19.00.23720.0

    0 讨论(0)
  • 2021-02-13 11:34

    MSVC doesn't know enough about User when it wants to calculate the type of properties to know it has a member age.

    We can work around the problem.

    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")
      );
    }
    

    In the JSON reflection code, simply replace std::decay_t<T>::properties with get_properties( tag<std::decay_t<T>> ).

    This has a few advantages. First you can retrofit some classes you do not own or wish to modify with properties seamlessly. With careful namespace use and ADL enabling the point of call, you can even do so for (some) types within std (with oublic members only; pair at the least).

    Second it avoids possible ODR-use requirements on properties. Properties is now a constexpr return value not some global data that may require storage.

    Third it permits properties to be written out-of-line with the class definition like above, or inline as a friend within the class, for maximium flexibility.

    0 讨论(0)
提交回复
热议问题