Цепные вызовы для временных в C ++ - PullRequest
1 голос
/ 27 сентября 2011

У меня есть класс, который выполняет преобразование строки, например,

class transer{
    transer * parent;
protected:
    virtual string inner(const string & s) = 0;
public:
    string trans(const string & s) {
        if (parent) 
            return parent->trans(inner(s));
        else 
            return inner(s);
    }
    transer(transer * p) : parent(p) {}

    template <class T>
    T create() { return T(this); }
    template <class T, class A1>   // no variadic templates for me
    T create(A1 && a1) { return T(this, std::forward(a1)); }
};

Так что я могу создать подкласс

class add_count : public transer{
    int count;
    add_count& operator=(const add_count &);
protected:
    virtual string inner(const string & s) { 
        return std::to_string((long long)count++) + s; 
    }
public:
    add_count(transer * p = 0) : transer(p), count(0) {}
};

И тогда я могу использовать преобразования:

void use_transformation(transer & t){
    t.trans("string1");
    t.trans("string2");
}
void use_transformation(transer && t){
    use_trasnformation(t);
}

use_transformation(add_count().create<add_count>());

Есть ли лучший дизайн для этого?Я хотел бы избежать использования динамического выделения / shared_ptr, если смогу, но я не уверен, будут ли временные объекты оставаться живыми на протяжении всего вызова.Я также хочу, чтобы каждый transer мог разговаривать со своим родителем во время уничтожения, поэтому временные также должны быть уничтожены в правильном порядке.Также сложно создать цепочечное преобразование и сохранить его на потом, так как

sometrans t = add_count().create<trans1>().create<trans2>().create<trans3>();

будет сохранять указатели для временных, которые больше не существуют.Делать что-то вроде

trans1 t1;
trans2 t2(&t1);
trans3 t3(&t2);

было бы безопасно, но раздражает.Есть ли лучший способ выполнять подобные операции в цепочке?

Ответы [ 2 ]

4 голосов
/ 27 сентября 2011

Временные будут уничтожены в конце полного выражения, в в обратном порядке они были построены. Будьте осторожны с последним, однако, поскольку нет никаких гарантий в отношении порядка оценка. (За исключением, конечно, прямой зависимости: если вы нужен один временный, чтобы создать следующий & mdash; а если я правильно понял, это ваш случай - тогда вы в безопасности.)

0 голосов
/ 28 сентября 2011

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

struct fooRef;  
struct foo  
{  
fooRef create() { return fooRef( m_Val ); }  
   foo& operator=( const fooRef& a_Other );  
   std::string m_Val;  
}  
struct fooRef  
{  
   fooRef( std::string& a_Val ) : m_Val( a_Val ) {}  
   fooRef create() { return fooRef( m_Val ); }  
   std::string& m_Val;  
}  
foo& foo::operator=( const fooRef& a_Other ) { m_Val = a_Other.m_Val; }  
foo startChain()  
{  
    return foo();  
}  
foo expr = startChain().create().create(); // etc

Сначала строка лежит на временном foo, созданном из startChain (), все связанные операции работают с этими исходными данными.Затем присваивание, наконец, копирует значение в именованную переменную.Вы, вероятно, можете почти гарантировать RVO на startChain ().

...