2017-01-24 4 views
0

considèrentComment un opérateur d'addition pour un conteneur de classe C++ peut-il être optimisé?

class abc 
    { 
    public: 
    map<int,double> data; 

    abc(map<int,double> && in):data(std::move(in)){} 
    abc operator + (const abc & in) 
    { 
    auto tmp = data; 
     for(auto & itr : in.data) 
      tmp[itr.first] += itr.second; 
     return abc(std::move(tmp)); 
    } 
    }; 

je suppose une meilleure mise en œuvre pourrait être

 abc operator + (const abc & in) 
    { 
     auto &tmp1 = (data.size() > in.data.size() ? data : in.data); 
     auto &tmp2 = (data.size() > in.data.size() ? in.data : data); 
     auto tmp = tmp1; 
     for(auto & itr : tmp2) 
      tmp[itr.first] += itr.second; 
     return abc(std::move(tmp)); 
    } 

Ce que je voudrais réaliser est que, si j'ai une déclaration, par exemple,

S = A+B+C+D+E 

et supposons B, C et D sont vides le coût devrait être identique àEssentiellement, je ne veux pas engager de coût si le type abc est égal à 0. Y a-t-il un moyen d'y parvenir?

+1

Oui, vous profitez du fait que les temporaires ne sont pas détruits jusqu'à la fin d'une expression complète. Donc vous faites 'operator + (..., ...)' renvoyer un objet proxy qui fait simplement référence aux objets. Laissez les proxies être "concat-able" avec eux-mêmes et votre objet par 'operator + (..., ...)'. Sur l'affectation finale du dernier proxy créé, vous faites l'addition en un seul coup de tout ce que les proxies ont lié ... – WhiZTiM

+0

@WhiZTim pourriez-vous fournir une illustration ou un lien pour montrer cette technique? – user6386155

Répondre

1

Je pense que vous allez faire mieux avec:

abc operator + (abc lhs, const abc& rhs) // Note 'lhs' by value. 
{ 
    for(const auto & val : rhs.data) 
     lhs.data[val.first] += val.second; 
    return lhs;         // Rely on NRVO 
} 

La raison de préférer prendre l'argument en termes de valeur, est que si elle est temporaire (comme, par exemple, le résultat de A + B) alors il y aura ne pas avoir besoin d'une copie. Le compilateur peut simplement passer le temporaire directement.

Modifier Un proxy comme suggéré par WhiZTiM sera beaucoup plus efficace car il remet tout à la fin. Il repose sur le fait que les temporaires ne sont pas détruits jusqu'à la fin de l'expression complète.

struct Proxy 
{ 
    std::vector<const abc*> values; 

    operator abc() { 
     assert(values.size() > 2); 
     abc result = *(values.back()); // Can't avoid one copy. 
     values.pop_back(); 
     for (const auto& v : values) 
      for (const auto& key_value : v->data) 
       result.data[key_value.first] += key_value.second; 

     return result; 
    } 
}; 

Proxy operator +(const abc &lhs, const abc& rhs) { 
    Proxy result; 
    result.values.push_back(&lhs); 
    result.values.push_back(&rhs); 
    return result; 
} 

Proxy operator +(Proxy lhs, const abc& rhs) { 
    lhs.values.push_back(&rhs); 
} 
Proxy operator +(const abc& lhs, Proxy rhs) { 
    rhs.values.push_back(&lhs); 
} 
Proxy operator +(Proxy lhs, const Proxy& rhs) { 
    // implementation left as an exercise for the reader. 
} 

Remarque: ci-dessus non exposée à un compilateur.

+0

est-ce que NRVO se produit sur l'argument de la fonction? – user6386155

+0

Ah. Bon point. Peut-être pas. Doit inspecter la sortie si –

+0

serait 'std :: move' au retour soit une meilleure optimisation? – user6386155