То, что я пытаюсь сделать, это иметь 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 не дало мне никакой лучшей подсказки.
Если кто-нибудь может указать мне на что-либо (предпочтительно раздел в стандарте), который доказывает это так или иначе (законно или нет), я был бы вечно благодарен.
Реализация у вас не является законной: доступ к foo::impl
является частным, то есть только определение foo
или его члены могут ссылаться на него. В файле реализации вы ссылаетесь на имя в области имен.
Соответствующей частью стандарта являются 11 [class.access] пункт 1 и пункт 4.
Я думаю, что это не разрешено. Хотя в стандарте есть примечание, что
Поскольку контроль доступа применяется к именам, если контроль доступа применяется к имени 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
определение скрыто).