C++11:抽象 const、易失性、左值引用和右值引用限定成员函数指针?

发布于 2024-12-27 12:12:46 字数 2257 浏览 1 评论 0原文

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

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 还允许 < code>& 和 &&& 理论上在 C++03 中是允许的,但事实并非如此)。

可以使用限定符的任意组合,但 &&& 是互斥的,这使得 C++ 中存在 2^2 = 4 种可能性C++11 中的 03 和 2^4-4 = 12。

当您想要使用成员函数指针时,这可能会很痛苦,因为它们在这些限定符中甚至没有一点多态性:成员函数指针的“this类型”上的限定符作为参数传递的参数必须与它所传递的参数类型完全匹配。 C++ 也没有提供明确的工具来抽象限定符。在 C++03 中,这基本上是可以的,因为您必须编写一个 const 版本和一个非 const 版本,并且没有人关心 易失性 code>,但在 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`

这比必须手动写出所有情况要好得多。

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

海湾合作委员会 4.7 说:

错误:形成指向引用类型“Baz&&”的指针

但这并不奇怪,因为 GCC 从 4.7 开始还不支持 this 上的引用限定符。

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

错误:成员指针引用非类类型“Baz &&”

那好吧。

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

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

C++03 lets you qualify function parameters as being const, volatile, and/or lvalue references (&).

C++11 adds one more: rvalue references (&&).

Furthermore, C++ lets you overload functions based on the qualifiers of their parameters, so that the most appropriate overload is selected when calling the function.

A member function can conceptually be thought of as a function which takes an extra parameter, whose type is a reference to an instance of the class of which it is a member. It's possible to overload a member function based on the qualifiers of this 'extra parameter' in much the same way as any other parameter. This is expressed by putting the qualifiers at the end of the function signature:

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
};

In C++03, const and volatile qualifiers are possible, and C++11 also allows & and && (& could theoretically have been allowed in C++03, but it wasn't).

Any combination of qualifiers can be used, with the exception that & and && are mutually exclusive, which makes for 2^2 = 4 possibilities in C++03 and 2^4-4 = 12 in C++11.

This can be quite a pain when you want to work with member function pointers, because they aren't even a little bit polymorphic in these qualifiers: the qualifiers on the "this type" of a member function pointer passed as an argument must exactly match those on the type of the parameter it's being passed as. C++ also offers no explicit facility to abstract over qualifiers. In C++03 this was mostly OK, because you would have to write a const version and a non-const version and no one cares about volatile, but in the pathological case in C++11 (which is not as uncommon as it is pathological) you could have to manually write as many as 12 overloads. Per function.

I was very happy to discover that if you are passing the type of the enclosing class as a template parameter and derive the type of a member function pointer from it, that const and volatile qualifiers are allowed and propagated as you would expect:

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`

This is a great deal nicer than having to write out all of the cases manually.

Unfortunately, it doesn't seem to work for & and &&.

GCC 4.7 says:

error: forming pointer to reference type ‘Baz&&’

But that's not too surprising, given that GCC as of 4.7 doesn't yet have support for reference qualifiers on this.

I also tried it with Clang 3.0, which does have such support:

error: member pointer refers into non-class type 'Baz &&'

Oh, well.

Am I correct in concluding that this is not possible, and that there's no way to abstract over reference qualifiers on the "this type" of member function pointers? Any other techniques for abstracting over qualifiers (especially on this) other than in the specific case when you're passing the "this type" as a template parameter would also be appreciated.

(It's worth pointing out that if C++ didn't distinguish between member functions and normal functions, this would all be trivial: you'd use the template parameter as the type of a parameter of the function (pointer), and the template argument would be passed through as-is, qualifiers intact, no extra thought necessary.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

您是否考虑过简单地专门化您的模板?

您只需添加两个版本即可:

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 次代码。

Have you thought about simply specializing your template ?

You can just add the two versions:

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

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

And then the compiler will pick the right specialization (or fallback to the general case) appropriately.

This saves you from the const/volatile thing, but does imply that you need to write the code 3 times.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文