2013-07-19 12 views
24

Considerare una semplice classe int Wrapper con moltiplicazione sovraccaricata operator*= e operator*. Per il sovraccarico dell'operatore "vecchio stile", è possibile definire operator* in termini di operator*= e ci sono anche librerie come Boost.Operators e la sua moderna incarnazione df.operators di @DanielFrey che riducono il boilerplate per voi.Linee guida per fare il sovraccarico dell'operatore di constexpr?

Tuttavia, per i calcoli in fase di compilazione utilizzando il nuovo C++ 11 constexpr, questa convenienza scompare. Un constexpr operator* non può chiamare operator*= perché quest'ultimo modifica il suo argomento (implicito) a sinistra. Inoltre, c'è no overloading on constexpr, quindi aggiungere constexpr operator* in più ai risultati esistenti di operator* in un'ambiguità di risoluzione del sovraccarico.

Il mio approccio attuale è:

#include <iostream> 

struct Wrap 
{ 
    int value;  

    Wrap& operator*=(Wrap const& rhs) 
    { value *= rhs.value; return *this; } 

    // need to comment this function because of overloading ambiguity with the constexpr version 
    // friend Wrap operator*(Wrap const& lhs, Wrap const& rhs) 
    // { return Wrap { lhs } *= rhs; }  

    friend constexpr Wrap operator*(Wrap const& lhs, Wrap const& rhs) 
    { return { lhs.value * rhs.value }; } 
}; 

constexpr Wrap factorial(int n) 
{ 
    return n? factorial(n - 1) * Wrap { n } : Wrap { 1 };  
} 

// want to be able to statically initialize these arrays 
struct Hold 
{ 
    static constexpr Wrap Int[] = { factorial(0), factorial(1), factorial(2), factorial(3) }; 
}; 

int main() 
{ 
    std::cout << Hold::Int[3].value << "\n"; // 6 
    auto w = Wrap { 2 }; 
    w *= Wrap { 3 }; 
    std::cout << w.value << "\n"; // 6 
} 

Live output here. I miei problemi con questo sono:

  • duplicazione della logica moltiplicazione sia operator*= e operator*, invece di operator* essere espresso in termini di operator*=
  • quindi, Boost.Operators non funziona più per ridurre il boilerplate per la scrittura di molti altri operatori aritmetici

domanda: è questo modo di avere sia un runtime operator*= e misti runtime raccomandata C++ 11/fase di compilazione 01.237.? C++ 14 modifica qualsiasi cosa qui ad es. ridurre la duplicazione logica?

UPDATE: La risposta da @AndyProwl è accettata come idiomatica, ma come da suggerimento di @DyP, in C++ 11 uno potrebbe ridurre la duplicazione logica a scapito di un incarico in più e lo stile intuitivo

// define operator*= in terms of operator* 
    Wrap& operator*=(Wrap const& rhs) 
    { *this = *this * rhs; return *this; } 
+0

Qual è il tuo utilizzo per un sovraccarico "regolare" se può essere "constexpr"? IIRC 'constexpr' si degraderà con grazia a _runtime execution_ nel contesto non-'constexpr'. – sehe

+1

@sehe non si può avere un operatore 'constexpr * =', e quindi 'operatore constexpr *' non può chiamare questo, e invece ha bisogno di duplicare la logica di estrazione dei campi ecc. – TemplateRex

+1

Ah, sto iniziando a vedere la tua vera domanda. Non è *** di avere sovraccarichi non di 'constexpr' (non ne hai bisogno!) Ma piuttosto di non essere in grado di condividere codice perché '* =' non può essere 'constexpr'.Per fortuna ho già fatto +1 su buona fede :) – sehe

risposta

17

non riuscivo a trovare una soluzione idiomatica per C++ 11 (anche se come soluzione alternativa, DyP's suggestion sembra accettabile per me).

In C++ 14 invece, dove constexpr does not imply const (vedi allegato C.3.1 del C++ 14 Progetto standard n3690), si può semplicemente definire sia operator *= e operator * come constexpr, e definire il secondo in termini di ex , come al solito:

struct Wrap 
{ 
    int value;  

    constexpr Wrap& operator *= (Wrap const& rhs) 
    { value *= rhs.value; return *this; } 

    friend constexpr Wrap operator * (Wrap const& lhs, Wrap const& rhs) 
    { return Wrap(lhs) *= rhs; }  
}; 

Ecco una live example, dove il programma di cui sopra è stato compilato con il -std=c++1y Clang - purtroppo, GCC non sembra applicare questa regola ancora.

+0

cosa ne pensi del suggerimento di DyP di invertire la dipendenza a costo di un incarico? – TemplateRex

+5

@TemplateRex: Sì, ci ho pensato anch'io, ma non l'ho postato come soluzione perché sarebbe andato contro la pratica standard. Ripristinare una linea guida quando una modifica di abilitazione è pronta per C++ 14 sarebbe IMO inappropriata. Tuttavia, come soluzione alternativa a C++ 11, dovrebbe essere accettabile. Onestamente non penso che le prestazioni sarebbero un problema qui - non sono un esperto di compilatori, ma mi aspetto che l'ottimizzatore esegua inline pesanti per funzioni così semplici. Inoltre, cerco di evitare l'ottimizzazione prematura ed evitare di rifiutare un design solo a causa di ipotesi sulle prestazioni. –

+0

Il 'constexpr' non implica più' const' aside, sono perplesso ** perché ** funziona: 'operator * =' è sia multilinea che muta 'this', ma viene richiamato durante la compilazione. Sono entrambe le restrizioni * rimosse * anche in C++ 14? Hai una citazione sulla carta di lavoro? – TemplateRex

Problemi correlati