2016-09-25 36 views
2

соратника шахты регулярно использует вариацию на Pimpl, что он делает так:pimpl idiom с использованием структуры анонимного пространства имен: это безопасно?

foo.h:

namespace { struct Impl; } 

class Foo 
{ 
public: 
    Foo(); 
    ~Foo(); 

    void Bar(int n); 
    /* ... */ 

private: 
    std::unique_ptr<Impl> _impl; 
}; 

То, что здесь происходит, что он вперед объявить класс реализации, чтобы быть в анонимное пространство имен. Затем он определит класс Impl внутри Foo.cpp.

Таким образом, определение структуры ::Impl будет доступно для единицы перевода Foo.cpp. Другой код включает Foo.h, будет предупреждать, потому что они явно не могут получить доступ к ::Impl, указанному в Foo.cpp. Но тогда нам они не нужны - это класс, предназначенный для использования только в Foo.cpp; мы не хотите это видно или известно в другом месте.

И в то время как мы можем, конечно, есть ситуации, когда на .cpp файла, включая несколько заголовков, каждый из которых объявляя свои собственные ::Impl структуры, они фактически не конфликтует, потому что структуры никогда не используются за пределами их соответствующих единиц перевода.

tl; dr: Это выглядит странно, вызывает предупреждения и выглядит так, как будто это может вызвать столкновения, но, похоже, на самом деле работает.


Все, что было сказано, я не комфортно с того, что возвышает предупреждения запеченные так глубоко в наш код (больше заголовков файлов это находится, тем труднее будет вынуть). Это также только тонна предупреждений.

Мой товарищ по команде стоит на этом, потому что он прост, содержит простые определения кода и позволяет использовать короткий, согласованный класс Impl по всему нашему коду.

Я не сторонник кодирования условностей; если это хорошая практика для нашего прецедента, я не против. Но я хотел бы чувствовать себя комфортно, что это безопасно и легко обслуживается, и в какой-то момент он не будет взорваться в наших лицах.

+0

Точка зрения, я * думаю *, что нет ':: Impl'. Использование ':: Impl' пытается получить доступ к символу' Impl' в * глобальном * пространстве имен, но оно не входит в глобальное пространство имен. Другой распространенный способ - сделать структуру 'Impl' частным членом самого класса. –

+6

Если это происходит в файле заголовка, который используется несколькими единицами перевода, я думаю, что это нарушение ODR. Каждое анонимное пространство имен отличается в каждой единицы перевода, поэтому класс Foo не будет иметь согласованного определения во всех единицах перевода. –

+0

См. Http://stackoverflow.com/a/23653494/951890 –

ответ

5

Класс Foo нарушает ODR. Каждый файл cpp считает, что его уникальный ptr содержит другой тип.

Нарушение ODR делает вашу программу плохо сформированной, не требуется диагностика.

Ваша программа может работать, но ее поведение полностью не определено стандартом C++.

Практическая проблема, которая может привести к тому, что компилятор может измениться под вашими ногами, а текущее неопределенное поведение («похоже, что это работает») изменится на что-то другое («отливки неожиданно», «коррумпированные таблицы типов», «компоновщик не связывает», «компилятор доказывает, что класс никогда не может использоваться вне его внедренной единицы перевода и стирает весь код в ваших функциях, как если бы они запускали его, был бы UB.») в качестве примеров, но нет никаких ограничений на как безумный он мог получить.

Иногда бывает полезно делать UB, стоит риска. Здесь я вижу нулевую выгоду.

Создайте namespace FooImpl или Foo_details и напишите Impl.