【问题标题】:How to detect if a method is virtual?如何检测方法是否是虚拟的?
【发布时间】:2014-05-19 14:51:19
【问题描述】:

我尝试制作一个特征来查找方法是否为virtual: (https://ideone.com/9pfaCZ)

// Several structs which should fail depending if T::f is virtual or not.
template <typename T> struct Dvf : T { void f() final; };
template <typename T> struct Dvo : T { void f() override; };
template <typename T> struct Dnv : T { void f() = delete; };

template <typename U>
class has_virtual_f
{
private:
    template <std::size_t N> struct helper {};
    template <typename T>
    static std::uint8_t check(helper<sizeof(Dvf<T>)>*);
    template<typename T> static std::uint16_t check(...);
public:
    static
    constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};

测试用例:

struct V  { virtual void f(); };
struct NV {         void f(); };
struct E  {                   };
struct F  { virtual void f() final; }; // Bonus (unspecified expected output)

static_assert( has_virtual_f< V>::value, "");
static_assert(!has_virtual_f<NV>::value, "");
static_assert(!has_virtual_f< E>::value, "");

但我得到了error: 'void Dvf&lt;T&gt;::f() [with T = NV]' marked final, but is not virtual
如果我不使用sizeof 并直接在check 中使用Dvf&lt;T&gt;*,我没有编译错误,但check 不会因为SFINAE 中的“坏”类型而被丢弃:(。

检测方法是否为virtual 的正确方法是什么?

【问题讨论】:

  • Sfinae 不会“查看”类的定义内部,它不在 “直接上下文” 中(参见 14.8.2/8)。
  • 我很感兴趣为什么你想知道这个。通常情况下,客户端应该只关心接口而不关心幕后是否有虚拟调度。
  • 我认为,您必须查看 dll 的 IL 代码才能确定调用是否是虚拟的..
  • @MatthieuM.:回复explicitly-hide-a-base-function 时,我会用更简洁的特征扩展我的解决方案。
  • @Jarod42 这种行为是预期的,因为sizeof(Dvf&lt;T&gt;) 实例化了Dvf&lt;T&gt;Dvf&lt;T&gt;* 没有。您不能在 SFINAE 测试期间实例化无效类,否则您会收到错误消息。至于正确的方法,不知道是否可以;尝试。

标签: c++ c++11 sfinae typetraits virtual-functions


【解决方案1】:

代码并不完美,但基本上通过了测试(至少在 wandbox 和 gcc 7 以来可用的所有 clang 中):

#include <type_traits>

template <class T>
using void_t = void;

template <class T, T v1, T v2, class = std::integral_constant<bool, true>>
struct can_be_compaired: std::false_type { };

template <class T, T v1, T v2>
struct can_be_compaired<T, v1, v2, std::integral_constant<bool, v1 == v2>>: std::true_type { };

template <class T, class = void>
struct has_virtual_f: std::false_type { };

template <class T>
struct has_virtual_f<T, void_t<decltype(&T::f)>>{
    constexpr static auto value = !can_be_compaired<decltype(&T::f), &T::f, &T::f>::value;
};

struct V  { virtual void f() { }      };
struct NV {         void f() { }      };
struct E  {                           };
struct F  { virtual void f() final{ } }; // Bonus (unspecified expected output)

int main() {
   static_assert( has_virtual_f< V>::value, "");
   static_assert(!has_virtual_f<NV>::value, "");
   static_assert(!has_virtual_f< E>::value, "");
   static_assert( has_virtual_f< F>::value, "");
}

[live demo]


理论上让trait飞起来的相关标准部分:[expr.eq]/4.3,[expr.const]/4.23

【讨论】:

  • If either is a pointer to a virtual member function, the result is unspecified. 但也不是说它不可能是真的。还是我读错了标准?我猜如果它未指定,它可能是真或假,它可能取决于编译器?
  • @dau_sama 但是:An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:(...) a relational or equality operator where the result is unspecified; (...)
  • @dau_sama 这意味着当两个指针指向虚拟方法时是否应该相等是未指定的,但是当在 constexpr 上下文中测试相等性时,编译器应该做什么是明确定义的 - 它不是一个核心常量表达式,所以它应该会导致替换失败。
【解决方案2】:

可能无法确定特定方法是否为virtual。我这么说是因为Boost project 多年来一直研究性状,但从未进行过这样的性状测试。

但是,在 C++11 中,或者使用 Boost library,您可以使用 is_polymorphic&lt;&gt; 模板来测试类型以查看该类型是否具有虚函数。请参阅std::is_polymorphic<>boost::is_polymorphic<> 以供参考。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-06
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    相关资源
    最近更新 更多