【问题标题】:Pointer to member: works in GCC but not in VS2015指向成员的指针:适用于 GCC 但不适用于 VS2015
【发布时间】:2016-12-29 10:27:57
【问题描述】:

我正在尝试实现一个“属性”系统来将 C++ 实例转换为 JSON,反之亦然。我从 Guillaume Racicot 在这个问题 (C++ JSON Serialization) 的回答中提取了部分代码并对其进行了简化。

这是我的操作方式。我有一个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 中工作?

【问题讨论】:

  • Property 构造函数接受一两个参数吗?
  • @pts 是的,我把代码放在这里:coliru.stacked-crooked.com/a/276ac099068579fd
  • 完全相同的代码在VS2015(更新3)中无法编译
  • 我认为 MSVC 在完全定义类之前不能看到它的内部(User::age 在解析 }; 之前是未知的)。
  • 至于MSVC或GCC是否正确,阅读2个答案here

标签: c++ c++11 visual-c++ visual-studio-2015 c++14


【解决方案1】:

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

class User
{
public:
    int age;

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

这是因为在定义成员函数时,类被认为是完全定义的。它还有一个可取的属性,即properties如果使用odr,则不需要单独定义。

【讨论】:

    【解决方案2】:

    当 MSVC 想计算 properties 的类型以知道它有一个成员 age 时,它对 User 的了解不够。

    我们可以解决这个问题。

    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&lt;T&gt;::properties 替换为 get_properties( tag&lt;std::decay_t&lt;T&gt;&gt; )。

    这有几个优点。首先,您可以改造一些您不拥有或希望无缝修改属性的类。通过谨慎使用命名空间和启用调用点的 ADL,您甚至可以对 std 中的(某些)类型执行此操作(仅限 oublic 成员;至少配对)。

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

    第三,它允许将属性与上面的类定义脱节写入,或者在类中内联为friend,以获得最大的灵活性。

    【讨论】:

      【解决方案3】:

      如果绝对需要在 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-27
        • 1970-01-01
        相关资源
        最近更新 更多