如何测试方法是否为const?

How to test if a method is const?

如何获取一个布尔值来指示已知方法是否具有 const 限定符?

例如:

struct A {
    void method() const {}
};

struct B {
    void method() {}
};

bool testA = method_is_const<A::method>::value; // Should be true
bool testB = method_is_const<B::method>::value; // Should be false

type_traits header 中,我找到了一个可以使用的 is_const 测试,但我需要方法类型,但我不确定如何获得它。

我试过了:std::is_const<decltype(&A::method)>::value 但它不起作用,我能理解为什么 (void (*ptr)() const) != const void (*ptr)())。

检查是否可以在 const 限定的左值上调用成员函数要简单得多。

template<class T>
using const_lvalue_callable_foo_t = decltype(std::declval<const T&>().foo());

template<class T>
using has_const_lvalue_callable_foo = std::experimental::is_detected<const_lvalue_callable_foo_t, T>;

冲洗并重复,但 std::declval<const T>() 除外,以检查是否可以在 const 限定的右值上调用所述函数。我想不出 const && 成员函数的好的用例,因此检测这种情况是否有意义值得怀疑。

参考当前Library Fundamentals 2 TS working draft如何实现is_detected


检查特定的成员函数指针类型是否指向具有特定 cv-qualifier-seq 的函数类型要复杂得多。每个 cv-qualifier-seq 需要 6 个部分特化(constconst volatile 不同 cv-qualifier-seq s), 仍然不能处理重载的成员函数或成员函数模板。草拟想法:

template<class T> 
struct is_pointer_to_const_member_function : std::false_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args...) const> : std::true_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args...) const &> : std::true_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args...) const &&> : std::true_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const> : std::true_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const &> : std::true_type {};

template<class R, class T, class... Args> 
struct is_pointer_to_const_member_function<R (T::*)(Args..., ...) const &&> : std::true_type {};

如果您希望 const volatile 也成为 true,请按照这些思路取消另外 6 个部分专业化。

std::is_const<decltype(&A::method)>::value 不起作用的原因是 const 成员函数不是 const(成员函数)。它不是 const 的顶级 const int vs int.

我们可以做的是使用 void_t 的类型特征来测试我们是否可以在 const T:

上调用 method
template <typename... >
using void_t = void;

template <typename T, typename = void>
struct is_const_callable_method : std::false_type { };

template <typename T>
struct is_const_callable_method<T, void_t<
    decltype(std::declval<const T&>().method())
    > > : std::true_type { };

Demo

在 C++20 中,事情变得容易多了,因为 concepts 已经标准化,包含了检测习语。

现在我们只需要写这个约束:

template<class T>
concept ConstCallableMethod = requires(const T& _instance) {
    { _instance.method() }
};

ConstCallableMethod 测试表达式 _instance.has_method() 的格式是否正确,因为 _instance 是 const-reference 类型。

鉴于你的两个 classes:

struct A {
    void method() const { }
};

struct B {
    void method() { }
};

A (ConstCallableMethod<A>) 的约束条件为 trueB 的约束条件为 false


如果你想测试method函数的return类型是否无效,你可以添加->void到像这样的约束:

template<class T>
concept ConstCallableMethodReturnsVoid = requires(const T& _instance) {
    { _instance.method() } -> void
};

如果你想更通用一点,你可以传入一个成员函数指针到概念并测试该函数指针是否可以用 const 实例调用(尽管这会少一些当你有过载时很有用):

template<class T, class MemberF>
concept ConstCallableMemberReturnsVoid = requires(const T& _instance, MemberF _member_function) {
    { (_instance.*_member_function)() } -> void
};

你可以这样称呼它:

ConstCallableMemberReturnsVoid<A, decltype(&A::method)>

这允许一些其他理论上的 class,例如 C,它有一个 const 方法,但它没有命名 method:

struct C
{
    void foobar() const{}
};

我们可以用同样的概念来测试:

ConstCallableMemberReturnsVoid<C, decltype(&C::foobar)>

Live Demo

创建类型特征以确定方法的常量性:

template<typename method_t>
struct is_const_method;

template<typename CClass, typename ReturnType, typename ...ArgType>
struct is_const_method< ReturnType (CClass::*)(ArgType...)>{
    static constexpr bool value = false;
};

template<typename CClass, typename ReturnType, typename ...ArgType>
struct is_const_method< ReturnType (CClass::*)(ArgType) const>{
    static constexpr bool value = true;
};