C++11:对const、volatile、左值引用和右值引用限定的成员函数指针进行抽象

C++11: Abstracting over const, volatile, lvalue reference, and rvalue reference qualified member function pointers?

本文关键字:引用 成员 函数 指针 抽象 const volatile C++11      更新时间:2023-10-16

C++03允许您将函数参数限定为constvolatile和/或左值引用(&)。

C++11又添加了一个:右值引用(&&)。

此外,C++允许您根据函数参数的限定符重载函数,以便在调用函数时选择最合适的重载。

成员函数在概念上可以被认为是一个接受额外参数的函数,其类型是对其所属类的实例的引用。可以根据这个"额外参数"的限定符重载成员函数,其方式与任何其他参数几乎相同。这可以通过将限定符放在函数签名的末尾来表示:

struct Foo
{
    int& data();             // return a non-const reference if `this` is non-const
    const int& data() const; // return a const reference if `this` is const
};

在C++03中,constvolatile限定符是可能的,C++11也允许&&&(理论上&在C++03中可以被允许,但事实并非如此)。

除了&&&互斥之外,可以使用任何限定符的组合,这使得C++03中有2^2=4的可能性,C++11中有2^ 4-4=12的可能性。

当你想使用成员函数指针时,这可能会很痛苦,因为它们在这些限定符中甚至没有一点多态性:作为参数传递的成员函数指针的"this类型"上的限定符必须与传递参数的类型上的限定词完全匹配。C++也不提供对限定符进行抽象的显式功能。在C++03中,这基本上是可以的,因为您必须编写const版本和非const版本,并且没有人关心volatile,但在C++11中的病理性情况下(这并不罕见,因为它是病理性的),您可能不得不手动编写多达12个重载。每个函数。

我很高兴地发现,如果您将封闭类的类型作为模板参数传递,并从中派生成员函数指针的类型,那么constvolatile限定符是允许的,并按您所期望的那样传播:

template<typename Object>
struct Bar
{
    typedef int (Object::*Sig)(int);
};
Bar<Baz>;                // Sig will be `int (Baz::*)(int)`
Bar<const Baz>;          // Sig will be `int (Baz::*)(int) const`
Bar<volatile Baz>;       // Sig will be `int (Baz::*)(int) volatile`
Bar<const volatile Baz>; // Sig will be `int (Baz::*)(int) const volatile`

这比必须手动写出所有案例要好得多。

不幸的是,它似乎不适用于&&&

GCC 4.7规定:

错误:正在形成指向引用类型"Baz&amp;'

但这并不太令人惊讶,因为GCC从4.7开始还不支持this上的参考限定符。

我也在Clang 3.0上尝试过,它确实有这样的支持:

错误:成员指针指向非类类型"Baz&amp;'

哦,好吧。

我得出的结论是正确的吗?这是不可能的,并且没有办法在成员函数指针的"this type"上抽象过引用限定符?除了将"this类型"作为模板参数传递的特定情况之外,任何其他用于对限定符进行抽象的技术(尤其是在this上)也将受到赞赏。

(值得指出的是,如果C++不区分成员函数和普通函数,这一切都将是微不足道的:您将使用模板参数作为函数(指针)的参数类型,模板参数将按原样传递,限定符完好无损,无需额外考虑。)

您有没有想过简单地专门化您的模板?

你可以添加两个版本:

template <typename Object>
struct Bar<Object&> {
  typedef int (Object::*Sig)(int)&;
};
template <typename Object>
struct Bar<Object&&> {
  typedef int (Object::*Sig)(int)&&;
};

然后编译器将适当地选择正确的专业化(或回退到一般情况)。

这将省去const/volatile的麻烦,但确实意味着您需要编写3次代码。