从C ++ 11继承私有嵌套类型是否合法?

时间:2012-11-14 16:50:54

标签: c++ c++11 language-lawyer pimpl-idiom

我想要做的是在我的库类中将可变大小的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标准副本并没有给我任何更好的线索。

如果任何人可以指出任何事情(最好是标准中的一部分)证明这种方式或其他方式(合法或不合法),我将永远感激。

2 个答案:

答案 0 :(得分:3)

我认为这是不允许的。虽然标准中有一个注释

  

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

所以在

struct block : base

可以访问名称base。但是,typedef本身使用名称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;

任何人都可以使用名称private_public_access,但他们没有定义,因此无法访问private_public_access::foo_impl(尽管他们可以自己定义以获取访问权限)。 ..)。虽然如果这是可以接受的,那么将名称foo::impl公开并且其定义隐藏起来就像它已经存在一样可能是可接受的(并且隐藏了private_public_access的定义。)

答案 1 :(得分:3)

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

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