質問

What I am trying to do is to have a variable-size POD as a Pimpl in my library class:

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

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

Then define several fixed-size implementations like this:

// .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 versions 4.6 and 4.7 have no issues compiling this with -std=c++0x -Wall -pedantic, but I am still fuzzy about legality of using private nested type name like that. Wading through my [maybe outdated draft] copy of C++11 standard did not give me any better clues.

If anybody can point me to anything (preferably a section in the standard) that proves this one way or the other (legal or not) I would be eternally grateful.

役に立ちましたか?

解決

The implementation you have is not legal: Access to foo::impl is private, i.e., only the definition of foo or its members can reference it. In the implementation file you reference the name at namespace scope.

The relevant part of the standard is 11 [class.access] paragraph 1 and paragraph 4.

他のヒント

I think that this isn't allowed. Although there's a note in the standard that

Because access control applies to names, if access control is applied to a typedef name, only the accessibility of the typedef name itself is considered. The accessibility of the entity referred to by the typedef is not considered.

So in

struct block : base

the name base is accessible. However the typedef itself uses the name foo::impl, which is private and therefore inaccessible. The name also would be inaccessible in the static_assert.

I don't see any exceptions that would allow the name to be accessed in these contexts.

My compiler produces these errors for this code:

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
           ^

One option might be to include a public friend in foo, which will have access to the private names inside foo. Then you can place the definition of that friend type in the cpp file so that the names it exposes are exposed only in that one file:

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

Anyone will be able to use the name private_public_access, but they won't have the definition and so won't be able to access private_public_access::foo_impl (although they could define it themselves to gain access...). Although if this is acceptable then maybe it'd be just as acceptable to leave the name foo::impl public and its definition hidden like it already is (and like private_public_access's definition is hidden).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top