4

我想要做的是在我的库类中将可变大小的 POD 作为 Pimpl:

// header file
class foo {
public:
    // ctors, copy, move, dtor, etc.

private:
    struct impl; // forward-declared
    impl* pimpl; // pointer to private implementation
};

然后像这样定义几个固定大小的实现:

// .cpp implementation file
struct foo::impl {
    uint32_t refs;
    uint32_t size;
    uint32_t len;
    uint32_t data;
};

static_assert( sizeof( typename foo::impl ) == 16, "paranoia" );

namespace { // anonymous
    typedef typename foo::impl base;

    template <size_t S>
    struct block : base {
        static_assert( S > 16, "invalid block size" );
        static_assert((( S - 1 ) & S ) == 0, "block size must be power of 2" );

        uint8_t pad[S - 16];
    };

    typedef block<64>  block64;
    typedef block<128> block128;
    // ...
}

// foo implementation using the above PODs

GCC 版本 4.6 和 4.7 用 编译这个没有问题-std=c++0x -Wall -pedantic,但我仍然对使用这样的私有嵌套类型名称的合法性感到模糊。翻阅我的 C++11 标准的 [可能是过时的草案] 副本并没有给我任何更好的线索。

如果有人能指出我的任何东西(最好是标准中的一个部分)可以证明这种方式或另一种方式(合法与否),我将永远感激不尽。

4

2 回答 2

4

我认为这是不允许的。虽然标准中有一条说明

因为访问控制适用于名称,如果访问控制适用于 typedef 名称,则仅考虑 typedef 名称本身的可访问性。不考虑 typedef 引用的实体的可访问性。

所以在

struct block : base

该名称base是可访问的。但是 typedef 本身使用 name foo::impl,这是私有的,因此无法访问。在 static_assert 中也无法访问该名称。

我没有看到任何允许在这些上下文中访问名称的异常。

我的编译器为此代码产生了这些错误:

main.cpp:16:27: error: 'impl' is a private member of 'foo'
static_assert(sizeof(foo::impl) == 16, "paranoia");
                          ^
main.cpp:4:12: note: declared private here
    struct impl; // forward-declared
           ^
main.cpp:19:27: error: 'impl' is a private member of 'foo'
    typedef typename foo::impl base;
                          ^
main.cpp:4:12: note: declared private here
    struct impl; // forward-declared
           ^

一种选择可能是在其中包含一个公共朋友foo,该朋友可以访问其中的私人名称foo。然后,您可以将该朋友类型的定义放在 cpp 文件中,以便它公开的名称仅在该文件中公开:

// header
struct foo {
    struct private_public_access;
private:
    struct impl;
};

// cpp
struct foo::impl {};

struct private_public_access {
    typedef foo::impl foo_impl;
};

typedef private_public_access::foo_impl base;

任何人都可以使用 name private_public_access,但他们没有定义,因此无法访问private_public_access::foo_impl(尽管他们可以自己定义它以获得访问权限......)。尽管如果这是可以接受的,那么将名称foo::impl公开并将其定义隐藏起来(并且 likeprivate_public_access的定义已隐藏)也许同样可以接受。

于 2012-11-14T17:53:52.443 回答
4

您拥有的实现是不合法的:访问foo::impl是私有的,即只有定义foo或其成员可以引用它。在实现文件中,您在命名空间范围内引用名称。

标准的相关部分是 11 [class.access] 第 1 段和第 4 段。

于 2012-11-14T17:55:53.407 回答