【问题标题】:Type trait: Check if reference member variable is static or not类型特征:检查引用成员变量是否为静态
【发布时间】:2016-04-19 06:12:26
【问题描述】:

我想检查一个类的成员变量是否是静态的。使用 std::is_member_pointer 对除引用成员之外的所有类型都有效。

#include <type_traits>

struct A {
  int foo;
};

struct B : A {};

struct C {
  static int foo;
};

struct D : C {
};

struct E {
  int &foo;
};

struct F {
  static int &foo;
};

static_assert(std::is_member_pointer<decltype(&A::foo)>::value, "No");
static_assert(std::is_member_pointer<decltype(&B::foo)>::value, "No");
static_assert(!std::is_member_pointer<decltype(&C::foo)>::value, "No");
static_assert(!std::is_member_pointer<decltype(&D::foo)>::value, "No");

// Fail to compile:
static_assert(std::is_member_pointer<decltype(&E::foo)>::value, "No");

static_assert(!std::is_member_pointer<decltype(&F::foo)>::value, "No");

Live example.

我理解错误,指针不能指向引用成员。但是如何避免它并且仍然区分它是静态变量还是非静态变量?有什么想法吗?

【问题讨论】:

标签: c++ c++11 reference typetraits


【解决方案1】:

如果&amp;E::foo 使用 SFINAE 失败,您可以添加一个后备(如果E::foo 根本不存在,则可以添加另一个):

template <typename T>
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int);

template <typename T>
decltype(T::foo, std::true_type{}) is_member_foo(long);

template <typename T>
std::false_type is_member_foo(...);

template <typename T>
using IsMemberFoo = decltype(is_member_foo<T>(0));

static_assert(IsMemberFoo<A>{}, "No");
static_assert(IsMemberFoo<B>{}, "No");
static_assert(!IsMemberFoo<C>{}, "No");
static_assert(!IsMemberFoo<D>{}, "No");
static_assert(IsMemberFoo<E>{}, "No");
static_assert(!IsMemberFoo<F>{}, "No");
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { };

这段代码的作用:

  • 如果&amp;T::foo 有效,它将检查该成员是否为静态成员或不使用std::is_member_pointer(您的版本)。
  • 如果&amp;T::foo 无效,它会退回到第二个重载(这里您可以确定foo 不是静态的,否则会选择第一个重载):
    • 如果T::foo 有效(存在成员),则返回std::true_type
    • 否则它会回退到最后一个重载并返回std::false_type

还要注意(感谢@iammilind)对于private 成员,T::foo 无效,因此将选择第三个重载。

ideone 上的工作示例:http://ideone.com/FILHbK

旁注(扩展解释):

  • &amp;T::foo 有效时,前两个重载有效,但选择第一个重载是因为int 完全匹配,而long 不是。
  • decltype(T::foo, std::true_type{})T::foo 仅在 T::foo 无效时“让 SFINAE”回退到第三个重载,但结果类型为 std::true_type,这要归功于逗号运算符。

如果你喜欢,你也可以创建一个通用版本(http://ideone.com/lzH2FB):

#define IsMember(MEM) \
template <typename T> \
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \
template<typename T> \
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \
template <typename T> \
std::false_type is_member_##MEM(...); \
template <typename T> \
using IsMember_##MEM = decltype(is_member_##MEM<T>(0));

// Instanciate IsMember_foo
IsMember(foo);

// Use it:
static_assert(IsMember_foo<A>{}, "No");

如果您想将所有内容封装在一个类中(没有is_member_ 函数),还可以查看这两个答案:

【讨论】:

  • 您的回答对我来说似乎是正确的。赞成它。您可以对示例代码进行一些更改吗? 1.C 重复了两次,但D 不存在。 2. 将typename C 替换为typename T,因为它会给读者造成混淆(已经有class C)。在示例代码和您的答案中。顺便说一句,您对 SFINAE 的理解非常好,您应该使用宏创建类似代码的库,而不是像上面那样创建独立代码。另请注意,它可能不适用于private 变量。
  • @iammilind 谢谢,我已经更新了答案和ideone代码。
  • 您创建通用成员的新方法很好。但是想象一下,由于它的排他性,它最终会创建大量的is_member_##MEM。因此,您应该考虑一种方法,您可以将其封装在一个类中并继续使用同一个类。即使您创建了如此多的此类模板,从技术上讲也没有错。但是,根本没有必要这样做。 class 的另一个好处是,您可以拥有一个bool value,它也可以用于打印/检查目的。
  • @iammilind 其实IsMember_foo是一个可以转换成bool的类,它要么是std::true_type要么是std::false_type(见en.cppreference.com/w/cpp/types/integral_constant),然后你可以转换它可以布尔、实例化、访问::value,等等。我犹豫是否按照您所说的将所有内容封装在一个类中,但是我将无法获得std::integral_constant 的“好处”(我认为这种方式没有性能问题,除非可能会发生名称冲突,因为函数没有主体,所以不会产生任何影响)。
  • @iammilind 如果有人想将所有内容封装在一个类中,我已经在相关主题中添加了指向您和我的答案的链接,因为这个答案已经很长了。
猜你喜欢
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 2017-06-15
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
相关资源
最近更新 更多