2013-07-19 11 views
24

Rozważ klasę prostą int Wrapper z przeciążonym mnożeniem operator*= i operator*. W przypadku "przeciążania operatorów" w starym stylu można zdefiniować operator* pod względem operator*=, a istnieją nawet biblioteki takie jak Boost.Operators i jego nowoczesne wcielenie df.operators autorstwa @DanielFrey, które zmniejszają dla Ciebie podstawę.Wytyczne dotyczące przeciążania operatora constexpr?

Jednak w przypadku obliczeń kompilacji przy użyciu nowego C++ 11 constexpr wygoda ta znika. A constexpr operator* nie może wywołać operator*=, ponieważ ten drugi modyfikuje jego (niejawny) lewy argument. Ponadto istnieje no overloading on constexpr, więc dodanie dodatkowej constexpr operator* do istniejącego operator* powoduje niejednoznaczność rozdzielczości przeciążenia.

Moje obecne podejście jest:

#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. Moje problemy z tym są:

  • powielanie logiki mnożenia zarówno operator*= i operator* zamiast operator* wyrażone w kategoriach operator*=
  • Stąd Boost.Operators nie działa w celu zmniejszenia Gotowa do pisania wielu inne operatory arytmetyczne

Pytanie: jest to zalecana C++ 11 sposobem posiadania zarówno run-time operator*= i mieszane run-time/w czasie kompilacji constexpr operator*? Czy C++ 14 zmienia wszystko tutaj, na przykład zmniejszyć powielanie logiki?

UPDATE: Odpowiedź przez @AndyProwl akceptuje idiomatyczne ale jak za sugestią @DyP w C++ 11 jeden mógłby ograniczenie powielania logiki kosztem dodatkowego przydziału i intuicyjne stylu

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

Jaki jest twój pożytek z "normalnego" przeciążenia, jeśli może to być "constexpr"? IIRC 'constexpr' będzie wdzięcznie degradować do _runtime execution_ in non -constexpr' kontekście. – sehe

+1

@she nie możesz mieć 'operatora constexpr * =', więc 'operator constexpr *' nie może tego wywołać, a zamiast tego musi duplikować logikę wyodrębniania pól itp. – TemplateRex

+1

Ach, zaczynam widzieć twoje prawdziwe pytanie. Nie jest *** o tym, że nie ma przeciążenia non-'constexpr' (nie potrzebujesz ich!), Ale raczej o braku możliwości udostępniania kodu, ponieważ '* =' nie może być 'constexpr'.Dobrze, że już + w dobrej wierze :) – sehe

Odpowiedz

17

nie mogłem znaleźć idiomatyczne rozwiązanie dla C++ 11 (chociaż jako obejście, DyP's suggestion wydaje się do zaakceptowania dla mnie).

w C++ 14 jednak, gdzie constexpr does not imply const (patrz załącznik C.3.1 o C++ 14 standardowym projekcie n3690), można po prostu określić zarówno operator *= i operator * jako constexpr i definiują ten ostatni pod względem byłego jak zwykle:

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; }  
}; 

Oto live example, gdzie powyższy program jest skompilowany z -std=c++1y na Clang - niestety, GCC nie wydają się jeszcze wdrożyć tę zasadę.

+0

co sądzisz o sugestii DyP o cofnięciu zależności kosztem zlecenia? – TemplateRex

+5

@TemplateRex: Tak, myślałem o tym sam, ale nie opublikowałem go jako rozwiązania, ponieważ byłoby to niezgodne ze standardową praktyką. Odwrócenie wskazówki, kiedy aktywna zmiana jest gotowa na C++ 14, byłoby niewłaściwe IMO. Jednak jako obejście C++ 11 powinno być dopuszczalne. Naprawdę nie sądzę, że wydajność byłaby tutaj problemem - nie jestem ekspertem od kompilatorów, ale oczekiwałbym, że optymalizator będzie wykonywał ciężką inline dla tak prostych funkcji. Staram się również unikać przedwczesnej optymalizacji i unikać odrzucania projektu tylko z powodu założeń dotyczących wydajności. –

+0

'constexpr' już nie implikuje' const' na bok, jestem zaintrygowany ** dlaczego ** to działa: 'operator * =' jest zarówno multilined jak i mutuje 'this', ale jest wywoływany podczas kompilacji. Czy oba ograniczenia * również * zostały usunięte w C++ 14? Czy masz cytat z papieru roboczego? – TemplateRex