Сделайте аргумент обязательным, не полагаясь на позицию

Скажем, класс Foo имеет две зависимости (Bar и Baz), и что создание Foo является ошибкой без предоставления их обоих. Внедрение в конструктор позволяет легко гарантировать во время компиляции, что это сделано:

class Foo
{
public:
Foo(const std::shared_ptr<Bar>& bar, const std::shared_ptr<Baz>& baz);
// (don't get hung up on the type of pointer used; it's for example only)
};

Но скажем, Foo также нужны две пары:

class Foo
{
public:
Foo(const std::shared_ptr<Bar>& bar, const std::shared_ptr<Baz>& baz,
double val1, double val2);
};

Теперь есть проблема; для вызывающей стороны было бы очень просто случайно транспонировать val1 и val2 и создать ошибку времени выполнения. Мы можем добавить структуру Params, чтобы разрешить именованную инициализацию и исключить это:

class Foo
{
public:
struct Params
{
std::shared_ptr<Bar> bar;
std::shared_ptr<Baz> baz;
double val1;
double val2
};

Foo(const Params& params);
};

// ...

std::shared_ptr<Foo> MakeDefaultFoo()
{
Foo::Params p;
p.bar = std::make_shared<Bar>();
p.baz = std::make_shared<Baz>();
p.val1 = 4.0;
p.val2 = 3.0;
return std::make_shared<Foo>(p);
}

Но теперь у нас есть проблема, заключающаяся в том, что вызывающая сторона может забыть заполнить одно из полей в Params, которое не будет обнаружено до времени выполнения. Синтаксис инициализации структуры или список инициализаторов не позволят забыть поле, но тогда мы вернемся к положению!

Есть ли какая-то хитрость, позволяющая получить лучшее из обоих миров — обязательные аргументы с применением компилятора, которые назначаются по имени вместо позиции?

0

Решение

Просто у простой обертки может получиться

template <typename Tag, typename T>
struct Argument {
explicit Argument( const T &val );
T get() const;
};

class Foo {
public:
struct Val1Tag;
struct Val2Tag;
typedef Argument<Val1Tag,double> Val1;
typedef Argument<Val2Tag,double> Val2;

Foo( Val1 v1, Val2 v2 );

};

Foo foo( Foo::Val1( 1.0 ), Foo::Val2( 2.3 ) );

Теперь типы являются явными, и вы не можете поменять их местами без получения ошибки компилятора.

4

Другие решения

Очень любопытно посмотреть, с чем возится cdhowie, но в то же время простая оболочка разных типов может решить некоторые проблемы:

struct Val1 {
explicit Val1(double v) : v(v) { }
operator double() const { return v; }

double v;
};

// copy for Val2

class Foo
{
public:
Foo(const std::shared_ptr<Bar>& bar, const std::shared_ptr<Baz>& baz,
Val1 val1, Val2 val2);
};

Таким образом, вы не можете смешивать их, так как вам нужно будет создать Foo как:

Foo foo(bar, baz, Val1{3.0}, Val2{7.0});

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

2

Как то так (не проверено)

template <typename tag, typename t>
struct param
{
explicit param(t vv)
: v(vv) {}
param(const param& p)
: v(p.v) {}
t v;
};

struct one{}; struct two {};
using paramone = param<one, double>;
using paramtwo = param<two, double>;

void somefunc (paramone p1, paramtwo p2)
{ ... };
void somefunc (paramtwo p2, paramone p1)
{ somefunc(p1, p2); }

// using it

somefunc (2, 3); // bad
somefunc (paramone(2), paramtwo(3)); // good
somefunc (paramtwo(3), paramone(2)); // also good
0
По вопросам рекламы [email protected]