【问题标题】:C++11: Abstracting over const, volatile, lvalue reference, and rvalue reference qualified member function pointers?C++11:对 const、volatile、左值引用和右值引用限定的成员函数指针进行抽象?
【发布时间】:2012-02-10 22:53:33
【问题描述】:

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 中 2^4-4 = 12 ++11.

当您想使用成员函数指针时,这可能会很痛苦,因为它们在这些限定符中甚至没有一点多态性:成员函数指针的“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`

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

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

GCC 4.7 说:

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

但这并不令人惊讶,因为 GCC 4.7 尚不支持 this 上的引用限定符。

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

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

哦,好吧。

我认为这是不可能的,并且没有办法在成员函数指针的“this type”上抽象出过度引用限定符,这是否正确?除了在特定情况下将“@​​987654345@ 类型”作为模板参数传递之外,任何其他用于抽象限定符的技术(尤其是在 this 上)也将不胜感激。

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

【问题讨论】:

  • 对于所有可能的限定符组合,您多久需要不同的行为? (或者,就此而言,甚至有两种或三种可能的组合?)
  • 如果我试图为类似函数的对象编写一个抽象(本着 std::function 的精神但不一样),那么完整性就是一个设计目标。我无法真正推断或估计(至于频率),但我知道我以前遇到过。

标签: c++ c++11 overloading member-function-pointers qualifiers


【解决方案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 次代码。

【讨论】:

  • 是的,constvolatile 被智能处理意味着我只需要编写三个版本而不是十二个版本,这很好。
猜你喜欢
  • 2016-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 2013-04-21
  • 1970-01-01
相关资源
最近更新 更多