Предположим, я пишу 3D-рендерер на C ++ 11, где я создаю материалы и присваиваю их модели.
Я хотел бы иметь возможность создавать материалы с использованием идиома именованных параметров,как это:
auto material = Material().color( 1, 0, 0 ).shininess( 200 );
Затем я могу создать модель, подобную этой:
auto model = Model( "path/to/model.obj", material );
Я также хочу иметь возможность передавать материал как значение r:
auto model = Model( "path/to/model.obj", Material() );
В этом простом сценарии использования я могу написать свой класс Model
следующим образом:
class Model {
public:
Model() = default;
Model( const fs::path &path, const Material &material )
: mPath( path ), mMaterial( material ) {}
private:
fs::path mPath;
Material mMaterial;
};
Вопрос
Я хочу сделать Material
абстрактным базовым классом,так что я могу создавать собственные реализации для определенных видов материалов.Это означает, что в моем Model
вместо этого я должен хранить указатель (или даже ссылку) на материал.Но тогда я больше не могу передавать материал как r-значение.
Я мог бы вместо этого использовать переменную-член std::shared_ptr<Material>
, но тогда становится намного сложнее использовать идиому именованного параметра, потому что как быЯ создаю материал в этом случае?
Кто-нибудь из вас может дать мне хороший совет?
Более подробный пример кода
class Material {
public:
virtual ~Material() = default;
virtual void generateShader() = 0;
virtual void bindShader() = 0;
};
class BasicMaterial : public Material {
public:
BasicMaterial() = default;
BasicMaterial( const BasicMaterial &rhs ) = default;
static std::shared_ptr<BasicMaterial> create( const BasicMaterial &material ) {
return std::make_shared<BasicMaterial>( material );
}
void generateShader() override;
void bindShader() override;
BasicMaterial& color( float r, float g, float b ) {
mColor.set( r, g, b );
return *this;
}
private:
Color mColor;
};
class Model {
public:
Model() = default;
Model( const fs::path &path, ...what to use to pass Material?... )
: mPath( path ), mMaterial( material ) {}
private:
Material mMaterial; // Error! Can't use abstract base class as a member.
Material* mMaterial; // We don't own. What if Material is destroyed?
Material& mMaterial; // Same question. Worse: can't reassign.
std::shared_ptr<Material> mMaterial; // This? But how to construct?
};
// Can't say I like this syntax much. Feels wordy and inefficient.
std::shared_ptr<Material> material =
BasicMaterial::create( BasicMaterial().color( 1, 0, 0 ) );
auto model = Model( "path/to/model.obj",
BasicMaterial::create( BasicMaterial().color( 0, 1, 0 ) );