Группировка аргументов функции и константность - PullRequest
2 голосов
/ 22 ноября 2011

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

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;
};

void foo1(my_container<int, char, float const> & my_cont)
{
}

void foo2(my_container<int const, char, float const> & my_cont)
{
    // This should NOT be allowed: we do mind something being const to be treated by the
    // called function as non-const.
    foo1(my_cont);
}

void foo3(my_container<int, char, float> & my_cont)
{
    // This should be allowed: we don't mind something being non-const to be treated by the
    // called function as const.
    foo2(my_cont);
}

Наша проблема в том, что foo2 вызывает foo1 без жалоб компилятора, и мы хотели бы прямо противоположного.Это вообще возможно реализовать с помощью шаблонов?Есть ли другая техника?

Ответы [ 3 ]

1 голос
/ 22 ноября 2011

Без ограничения неопределенного поведения, это может быть достигнуто с другим уровнем косвенности.Добавьте класс представления, который ссылается на оригинальные члены.Константность может быть добавлена ​​неявно, но не может быть удалена.

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;
};

template <class A, class B, class C>
class my_container_view
{
    A* a_;
    B* b_;
    C* c_;

public:
    template <class A_, class B_, class C_>
    my_container_view(my_container<A_, B_, C_>& source):
        a_(&source.a), b_(&source.b), c_(&source.c)
    {}
    template <class A_, class B_, class C_>
    my_container_view(my_container_view<A_, B_, C_>& source):
        a_(&source.a()), b_(&source.b()), c_(&source.c())
    {}
    A& a() const { return *a_; }
    B& b() const { return *b_; }
    C& c() const { return *c_; }
};

void foo1(my_container_view<int, char, float const> my_cont)
{
    my_cont.a() = 10;
    my_cont.b() = 'a';
    my_cont.c() /*= 3.14*/;
}

void foo2(my_container_view<int const, char, float const> my_cont)
{
    my_cont.a() /*= 10*/;
    my_cont.b() = 'a';
    my_cont.c() /*= 3.14*/;

    //foo1(my_cont); //not allowed
}

void foo3(my_container_view<int, char, float> my_cont)
{
    my_cont.a() = 10;
    my_cont.b() = 'a';
    my_cont.c() = 3.14;
t
    foo2(my_cont);
}

int main()
{
    my_container<int, char, float> mc;
    foo1(mc);
    foo2(mc);
    foo3(mc);
}

(хотя я сомневаюсь, сколько это стоит. Обычно с классами, вы можете изменить все его члены - и вы простоне изменяйте те, которые вам не нужны - или вы не можете их модифицировать. Если вам нужен этот уровень контроля, вы бы предпочли передавать каждый аргумент отдельно - в противоположность тому, что вы делаете.)

1 голос
/ 22 ноября 2011

Ни один не должен работать.Различные экземпляры шаблона - это не связанные типы, без неявных преобразований между ними.Таким образом, my_container<int, char, float const>, my_container<int const, char, float const> и my_container<int, char, float> - это все несвязанные типы без неявных преобразований между ними.

Должна быть возможность что-то решить, используя наследование, используя приемы метапрограммирования, чтобы определить, что вы наследуете от, но я не уверен, как, и я подозреваю, что это будет больше усилий, чем оно того стоит.

0 голосов
/ 22 ноября 2011

Мое решение с небольшим количеством метапрограммирования.Выглядит довольно некрасиво и не полностью проверено, но в любом случае: таким образом, все должно работать в целом "из коробки"

#include <iostream>
#include <boost/type_traits.hpp>

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;

    template<typename An, typename Bn, typename Cn>
    operator my_container<An,Bn,Cn>& ()
    {
        /* First, check whether compatible at all */
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<A>::type, typename boost::remove_cv<An>::type>::value));
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<B>::type, typename boost::remove_cv<Bn>::type>::value));
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<C>::type, typename boost::remove_cv<Cn>::type>::value));
        /* Enforce const'ness */
        BOOST_STATIC_ASSERT( !boost::is_const<A>::value || boost::is_const<An>::value );
        BOOST_STATIC_ASSERT( !boost::is_const<B>::value || boost::is_const<Bn>::value );
        BOOST_STATIC_ASSERT( !boost::is_const<C>::value || boost::is_const<Cn>::value );

        return *reinterpret_cast< my_container<An,Bn,Cn>* >(this);
    }
};

void foo1(my_container<int, char, float const> & my_cont)
{
}

void foo2(my_container<int const, char, float const> & my_cont)
{
    // This should NOT be allowed: we do mind something being const to be treated by the
    // called function as non-const.
    //foo1(my_cont); /// BOOST_STATIC_ASSERT fails! Hurray!
}

void foo3(my_container<int, char, float> & my_cont)
{
    // This should be allowed: we don't mind something being non-const to be treated by the
    // called function as const.
    foo2(my_cont); /// No complaints! Hurray!
}

int main(int argc, char* argv[])
{
    my_container<int,char,float> foobar;

    foo3(foobar);

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...