Белгілі бір түрдегі элементтердің кез-келген контейнерін өңдейтін қалыпты емес функция

Мен тақырыпта сипатталғандай функцияны алғым келеді.

Кез келген түрдегі (int, қос) элементтері бар кез келген түрдегі контейнерлермен (тізім, вектор, т.б.) жұмыс істейтін STL алгоритмдері итератор түрлерін үлгі параметрлері ретінде пайдалану арқылы жалпылықты қамтамасыз етеді, мысалы:

template
inline _OI
copy(_II __first, _II __last, _OI __result)

Бұл алгоритм элементтердің кез-келген түріне дейін жұмыс істегенге дейін жақсы әдіс. Элемент түріне арналған жалғыз талап - көшірме конструкторы болуы керек.

Бірақ, бізде бір нақты тип бар

class MyElement
{
    public:
    void doSomethingWithElement();
};

және функцияны іске қосқымыз келеді, бұл функцияны doSomethingWithElement() функциясына шақыру арқылы осы түрдегі элементтердің санын өңдейді.

Арнайы түрдегі контейнерді алатын функцияны жазу өте ыңғайлы емес, себебі көптеген контейнерлер бірдей түрде өңделеді (мысалы, иераторлар) және әр түрлі түрдегі контейнерлерді өңдеу қажет болса, біз кодты қайталауға мәжбүр боламыз. Үлгіні жазу жақсы жұмыс істейді, бірақ ол ұнамсыз болып көрінеді, өйткені біз функцияны (тақырып файлында) жариялаған орынды іске асыруымыз керек. Сондай-ақ, біз тек бір түрдегі элементтерді өңдеуді қаласаңыз, осы түрді параметрлеу мақсатқа жетудің дұрыс жолы емес.

Мен пайдаланатын итератор интерфейсі туралы ойланамын

void processContainer(IIterator begin, IIterator end);

Егер бұл иератордың таза виртуалды операторы ++ және оператор * болған болса, біз осындай объектілерді processContainer -қа жібере аламыз. Бірақ мәселе бар: егер IIterator абстрактілі сынып болса, оны processContainer енгізу кезінде құрастыра алмаймыз және егер біз IIterator-ға көрсеткішті өткізсек, бұл функция оны өзгерте алады.

Мұны істеу үшін кез-келген басқа бұзуды кім біледі? Немесе жоғарыда айтылғандарға қарағанда, өзгеше тәсіл болар еді? Алдын-ала рақмет.

1
Үлгілерді пайдаланыңыз және хакеттерді іздеңіз. Бұл оңай.
қосылды автор Captain Obvlious, көзі
@rwong Бұл хабарлама функцияны экспорттау немесе COM арқылы үлгілерді пайдалану немесе ABI-ге интерфейс жасау туралы емес, дәл сіздің нүктеңіз не? Мен сондай-ақ шаблондарды қолдану арқылы тыйым орталарына неге сенесің?
қосылды автор Captain Obvlious, көзі
@rwong Бұл хабарлама функцияны экспорттау немесе COM арқылы үлгілерді пайдалану немесе ABI-ге интерфейс жасау туралы емес, дәл сіздің нүктеңіз не? Мен сондай-ақ шаблондарды қолдану арқылы тыйым орталарына неге сенесің?
қосылды автор Captain Obvlious, көзі
@CaptainObvlious С-стильді ортақ кітапхана функциясын немесе Microsoft COM, немесе басқа тұрақты ABI шетел функционалды интерфейсін экспорттау сияқты көптеген жағдайлар бар, бұл үлгілерге тыйым салады.
қосылды автор rwong, көзі
Жалпы айтқанда, C ++ емес идиоматсыз тілдерге экспорттау туралы сөйлескенде, бірнеше басқа тілдерде жұп итераторлар анықтаған ауқым түсінігі бар екенін есте сақтаңыз. Оның орнына, көптеген тілдерде ағымдағы элементтің ауысуына, келесі элементке өтуге, позицияны қалпына келтіруге және есептік мәнді қайтаруға арналған «санауыш объектісі» бар («әмірлермен» шатастырмау керек). Егер қандай да бір себептермен жинаққа арналған адаптер жасасаңыз, адаптер екі соңғы нүктеден гөрі жинақ немесе ауқымды көрсету үшін саналы түрде жасалған.
қосылды автор rwong, көзі
Жалпы айтқанда, C ++ емес идиоматсыз тілдерге экспорттау туралы сөйлескенде, бірнеше басқа тілдерде жұп итераторлар анықтаған ауқым түсінігі бар екенін есте сақтаңыз. Оның орнына, көптеген тілдерде ағымдағы элементтің ауысуына, келесі элементке өтуге, позицияны қалпына келтіруге және есептік мәнді қайтаруға арналған «санауыш объектісі» бар («әмірлермен» шатастырмау керек). Егер қандай да бір себептермен жинаққа арналған адаптер жасасаңыз, адаптер екі соңғы нүктеден гөрі жинақ немесе ауқымды көрсету үшін саналы түрде жасалған.
қосылды автор rwong, көзі

8 жауаптар

Қарапайым тәсіл - шектеулерді елемеу және функцияны кез келген идентификаторы үшін үлгі ретінде орындау. Егер итератор типке сілтеме жасамаса, онда пайдаланушыға «X түрінде doSomethingWithElement мүше функциясы» жолдарында қате қате туралы хабар келеді.

Келесі static_assert функциясын қамтамасыз ету болар еді, бұл функция кез-келген иераторды (ол кез келген түрдегі жүктемені шектеуге қатысады) білдіреді, бірақ қате туралы хабар сәл көбірек ақпарат болады.

Сонымен қатар, сіз SFINAE-ны кандидаттар тізімінен жүктеуді алып тастауды қолдану туралы шешім қабылдай аласыз. Бірақ SFINAE әдемі алтын балға болса да, қолыңызда қолайлы тырнақ бар екеніне сенімді емеспін.

Егер сіз шынымен ары қарай төменге қарай жүргіңіз келсе, any_iterator үлгіні болдырмау үшін итератор түрінде өшіру әдісін орындау туралы мысал ретінде Adobe кітапханаларында. Бұл тәсілдің күрделілігі - бұрынғылардың кез-келгеніне қарағанда жоғары шамалардың болуы және жұмысты орындау құны ақырлы ABI-нің артықшылығын қамтамасыз етеді және кодының аз мөлшерін қамтамасыз етеді (әртүрлі итераторлар бір функцияға берілуі мүмкін).

3
қосылды
@Xeo: Ақпарат үшін рахмет, бірақ мен әлі де бұл шешімді шешу үшін қарағанда қиынырақ шешім іздейді деп ойлаймын :)
қосылды автор David Rodríguez - dribeas, көзі
boost :: any_range - boost :: any_range - boost :: range_detail :: any_iterator - де бар, бірақ мен шынымен бұл егжей. : /
қосылды автор Xeo, көзі
Әрине - бірақ егер сіз белгілі бір түрлерде ( std :: function , виртуалды интерфейстер немесе басқа нәрсе арқылы) шектелген интерфейс болса, any_iterator қолайлы .
қосылды автор Xeo, көзі

Қарапайым тәсіл - шектеулерді елемеу және функцияны кез келген идентификаторы үшін үлгі ретінде орындау. Егер итератор типке сілтеме жасамаса, онда пайдаланушыға «X түрінде doSomethingWithElement мүше функциясы» жолдарында қате қате туралы хабар келеді.

Келесі static_assert функциясын қамтамасыз ету болар еді, бұл функция кез-келген иераторды (ол кез келген түрдегі жүктемені шектеуге қатысады) білдіреді, бірақ қате туралы хабар сәл көбірек ақпарат болады.

Сонымен қатар, сіз SFINAE-ны кандидаттар тізімінен жүктеуді алып тастауды қолдану туралы шешім қабылдай аласыз. Бірақ SFINAE әдемі алтын балға болса да, қолыңызда қолайлы тырнақ бар екеніне сенімді емеспін.

Егер сіз шынымен ары қарай төменге қарай жүргіңіз келсе, any_iterator үлгіні болдырмау үшін итератор түрінде өшіру әдісін орындау туралы мысал ретінде Adobe кітапханаларында. Бұл тәсілдің күрделілігі - бұрынғылардың кез-келгеніне қарағанда жоғары шамалардың болуы және жұмысты орындау құны ақырлы ABI-нің артықшылығын қамтамасыз етеді және кодының аз мөлшерін қамтамасыз етеді (әртүрлі итераторлар бір функцияға берілуі мүмкін).

3
қосылды
@Xeo: Ақпарат үшін рахмет, бірақ мен әлі де бұл шешімді шешу үшін қарағанда қиынырақ шешім іздейді деп ойлаймын :)
қосылды автор David Rodríguez - dribeas, көзі
boost :: any_range - boost :: any_range - boost :: range_detail :: any_iterator - де бар, бірақ мен шынымен бұл егжей. : /
қосылды автор Xeo, көзі
Әрине - бірақ егер сіз белгілі бір түрлерде ( std :: function , виртуалды интерфейстер немесе басқа нәрсе арқылы) шектелген интерфейс болса, any_iterator қолайлы .
қосылды автор Xeo, көзі

Не істегіңіз келетінін нақты жасай алмайсыз. Функцияның қол жетімділігін шектеу үшін enable_if функциясын қолдануға болады:

template < typename Container >
typename enable_if,void>::type processContainer(Container c)...
0
қосылды

Не істегіңіз келетінін нақты жасай алмайсыз. Функцияның қол жетімділігін шектеу үшін enable_if функциясын қолдануға болады:

template < typename Container >
typename enable_if,void>::type processContainer(Container c)...
0
қосылды

Итератор интерфейсін өзгертпестен толық иератордың функционалдығы - оның көшірмесін жасау мүмкіндігін қоса, дерексіз идентификатор жасау мүмкін емес. Дегенмен, дерексіз базалық класс арқылы иератор функциясының ішкі жиынын іске асыра аласыз:

#include 
#include 
#include 


template
struct AbstractIterator
{
    virtual bool operator!=(const AbstractIterator& other) const = 0;
    virtual void operator++() = 0;
    virtual T& operator*() = 0;
};

template
struct ConcreteIterator : AbstractIterator::value_type>
{
    typedef typename std::iterator_traits::value_type value_type;
    Iterator i;
    ConcreteIterator(Iterator i) : i(i)
    {
    }
    virtual bool operator!=(const AbstractIterator& other) const
    {
        return i != static_cast(&other)->i;
    }
    virtual void operator++()
    {
        ++i;
    }
    virtual value_type& operator*()
    {
        return *i;
    }
};

template
ConcreteIterator wrapIterator(Iterator i)
{
    return ConcreteIterator(i);
}


class MyElement
{
public:
    void doSomethingWithElement();
};

void processContainerImpl(AbstractIterator& first, AbstractIterator& last)
{
    for(; first != last; ++first)
    {
        (*first).doSomethingWithElement();
    }
}

template
void processContainer(Iterator first, Iterator last)
{
    ConcreteIterator wrapFirst = wrapIterator(first);
    ConcreteIterator wrapLast = wrapIterator(last);
    return processContainerImpl(wrapFirst, wrapLast);
}

int main()
{
    std::vector v;
    processContainer(v.begin(), v.end());

    std::list l;
    processContainer(l.begin(), l.end());
}
0
қосылды
Рахмет, бұл қызықты шешім, бірақ ол processContainer үлгісін қайтадан қамтиды.
қосылды автор Oleg Andriyanov, көзі
processContainer іске асырылуы - бұл айналасында ғана орам. Қабыршақ тек қана итератордың кез келген түрімен тікелей қоңырау шалуға арналған.
қосылды автор willj, көзі

Итератор интерфейсін өзгертпестен толық иератордың функционалдығы - оның көшірмесін жасау мүмкіндігін қоса, дерексіз идентификатор жасау мүмкін емес. Дегенмен, дерексіз базалық класс арқылы иератор функциясының ішкі жиынын іске асыра аласыз:

#include 
#include 
#include 


template
struct AbstractIterator
{
    virtual bool operator!=(const AbstractIterator& other) const = 0;
    virtual void operator++() = 0;
    virtual T& operator*() = 0;
};

template
struct ConcreteIterator : AbstractIterator::value_type>
{
    typedef typename std::iterator_traits::value_type value_type;
    Iterator i;
    ConcreteIterator(Iterator i) : i(i)
    {
    }
    virtual bool operator!=(const AbstractIterator& other) const
    {
        return i != static_cast(&other)->i;
    }
    virtual void operator++()
    {
        ++i;
    }
    virtual value_type& operator*()
    {
        return *i;
    }
};

template
ConcreteIterator wrapIterator(Iterator i)
{
    return ConcreteIterator(i);
}


class MyElement
{
public:
    void doSomethingWithElement();
};

void processContainerImpl(AbstractIterator& first, AbstractIterator& last)
{
    for(; first != last; ++first)
    {
        (*first).doSomethingWithElement();
    }
}

template
void processContainer(Iterator first, Iterator last)
{
    ConcreteIterator wrapFirst = wrapIterator(first);
    ConcreteIterator wrapLast = wrapIterator(last);
    return processContainerImpl(wrapFirst, wrapLast);
}

int main()
{
    std::vector v;
    processContainer(v.begin(), v.end());

    std::list l;
    processContainer(l.begin(), l.end());
}
0
қосылды
Рахмет, бұл қызықты шешім, бірақ ол processContainer үлгісін қайтадан қамтиды.
қосылды автор Oleg Andriyanov, көзі
processContainer іске асырылуы - бұл айналасында ғана орам. Қабыршақ тек қана итератордың кез келген түрімен тікелей қоңырау шалуға арналған.
қосылды автор willj, көзі

You could use std::for_each(): http://www.cplusplus.com/reference/algorithm/for_each/

толық код:

void callDoSomething(MyElement &elem)
{
    elem.doSomething();
}

int main()
{
  std::vector vec(100);
  std::for_each(vec.begin(), vec.end(), callDoSomething);
}
0
қосылды

You could use std::for_each(): http://www.cplusplus.com/reference/algorithm/for_each/

толық код:

void callDoSomething(MyElement &elem)
{
    elem.doSomething();
}

int main()
{
  std::vector vec(100);
  std::for_each(vec.begin(), vec.end(), callDoSomething);
}
0
қосылды