2015-06-10 14 views
5

Biorąc pod uwagę następujące zdefiniowany przez użytkownika typ S z funkcji konwersji do podwójnym:Czy można przekazać typy niearametryczne jako argumenty do funkcji cmath?

struct S 
{ 
    operator double() { return 1.0;} 
}; 

oraz następujące wywołania cmath funkcje wykorzystujące typ S:

#include <cmath> 

void test(S s) { 
    std::sqrt(s); 
    std::log(s); 
    std::isgreater(s,1.0); 
    std::isless(s,1.0); 
    std::isfinite(s) ; 
} 

Ten kod kompiluje z gcc użycie libstdc++ (see it live) ale z clang przy użyciu libc++ generuje błędów dla kilku połączeń (see it live) z następującym błędem isgreater:

error: no matching function for call to 'isgreater' 
    std::isgreater(s,1.0); 
    ^~~~~~~~~~~~~~ 

note: candidate template ignored: disabled by 'enable_if' [with _A1 = S, _A2 = double] 
std::is_arithmetic<_A1>::value && 
^ 

i podobnych błędów dla isless i isfinite, więc libc++ spodziewa argumenty dla tych połączeń będzie arithmetic types który S jest nie, możemy to potwierdzić, przechodząc do źródła libc++ cmath header. Chociaż wymóg typów arytmetycznych nie jest spójny we wszystkich funkcjach cmath w .

Pytanie brzmi, czy można przekazać funkcje niearametryczne jako argumenty do funkcji cmath?

Odpowiedz

7

TL; DR

Zgodnie z normą jest to ważne, aby przejść non-arytmetycznych typów jako argumenty cmath funkcji ale wada zgłosić 2068 twierdzi oryginalny intencją było to, że cmath funkcje powinny być ograniczone do arytmetycznych rodzajów i wydaje się możliwe, że użycie niearametrycznych argumentów zostanie ostatecznie źle sformułowane. Tak więc, choć technicznie poprawne użycie niearytmetycznych typów jako argumentów wydaje się wątpliwe w świetle raportu o usterkach 2068.

Szczegóły

Nagłówek cmath jest pokryta projektu normy części 26.8[c.math] zapewnia dodatkową float i długi podwójne przeciążenia dla każdej funkcji określonej w math.h które ma następnie argument podwójny, akapit 11 zapewnia dostateczne przeciążenie i mówi:

Ponadto, nie będzie dodatkowych przeciążeń wystarczające do zapewnienia:

  1. Jeżeli którykolwiek argumentem odpowiadające podwójnym parametrem jest typu long double, wszystkie argumenty odpowiadające podwójnych parametrów efektywnie oddane do long double.
  2. W przeciwnym razie, jeśli jakikolwiek argument odpowiadający podwójnemu parametrowi ma typ double lub typ całkowity, wówczas wszystkie argumenty odpowiadające podwójnym parametrom są skutecznie rzutowane na podwójne.
  3. W przeciwnym razie wszystkie argumenty odpowiadające podwójnym parametrom są skutecznie rzutowane na zmienną.

Wydaje ważne w C++ 11

w C++ 11 sekcji 26.8[c.math] nie zawiera żadnych ograniczeń, wykluczający zakaz arytmetyczne argumenty cmath funkcji. W każdym przypadku od pytania mamy dostępne przeciążenie, które przyjmuje argumenty w postaci podwójnej i należy je wybrać przez overload resolution.

raport Defect 2086

Ale dla C++ 14 mamy defect report 2086: Overly generic type support for math functions, który twierdzi, że pierwotna intencja sekcji 26.8[c.math] było ograniczenie cmath funkcje są ważne wyłącznie dla arytmetyczne rodzaje, które naśladują jak oni pracowali w C:

Mam wrażenie, że ten zestaw zasada jest prawdopodobnie bardziej g eneric jako przeznaczone, moim założeniem jest, że jest napisane, aby naśladować zestaw reguł C99/C1x w 7,25 p2 + 3 w sposób "C++" [...] (należy zauważyć, że ograniczenia C prawidłowy zestaw do typów, które C++ opisuje jako typy arytmetyczne, ale zobaczyć poniżej jednej ważnej różnicy) [...]

i mówi:

Moja obecna propozycja, aby rozwiązać te problemy byłoby ograniczyć poprawnych typów argumentu tych funkcji do typów arytmetycznych.

i przeredagowany sekcja 26.8 ustęp 11 powiedzieć (podkr):

Ponadto, nie będzie dodatkowych przeciążeń wystarczająca do zapewnienia:

  1. Jeżeli którykolwiek arytmetyczną argumentu odpowiadający podwójnemu parametrowi ma typ długi podwójny, następnie wszystkie arytmetyczne argumentów odpowiadających dwóm parametrom jest skutecznie rzutowanych na długie podwójne.
  2. Inaczej, jeśli arytmetyczna argumentu odpowiadającej podwójnym parametrem jest typu dwu- lub typ całkowitą, wszystkie arytmetyczne argumenty odpowiadające podwójnych parametrów skutecznie przesyłać podwójne.
  3. W przeciwnym razie wszystkie arytmetyczne argumenty odpowiadające podwójnych parametrów są skutecznie rzucić do skutecznie rzucić do mieć typ pływaka.

Więc to jest nieważne w C++ 14?

Cóż, pomimo intencji wygląda technicznie nadal ważne jak twierdzi w niniejszym komentarzu z dyskusji w libc++ bug report: incorrect implementation of isnan and similar functions:

które mogły być intencje, ale nie widzę żadnych sposób przeczytaj w ten sposób sformułowanie standardu . Z przykład w komentarzu # 0:

std::isnan(A()); 

Brak argumentów typu arytmetycznego, więc żaden z pocisków w 26,8/11 stosuje się. Zestaw przeciążeniowy zawiera "isnan (float)", "isnan (double)" i "isnan (long double)", a "isnan (float)" powinno być wybrane .

Tak, przeredagowanie przez DR 2086 ust nie robi to źle sformułowane, aby zadzwonić do pływak, podwójne i long double przeciążeń dostępne w inny sposób z non-arytmetycznych argumentów.

poprawny technicznie, ale wątpliwe, aby korzystać

Więc chociaż standard C++ 11 i C++ 14 nie ograniczają cmath funkcje do arytmetyki argumenty DR 2068 twierdzi intencją 26.8 ust 11 było ograniczenie cmath funkcje brać tylko argumenty arytmetyczne i najwyraźniej zamierzał zamknąć lukę w C++ 14, ale nie zapewnił wystarczająco silnych ograniczeń.

Wydaje się wątpliwe, aby polegać na funkcji, która mogłaby się źle uformować w przyszłej wersji standardu. Ponieważ mamy rozbieżność w implementacji, każdy kod, który polega na przekazywaniu nieparametrycznych argumentów do funkcji cmath dla tych przypadków, jest nieprzenośny i będzie użyteczny tylko w ograniczonych sytuacjach. Mamy alternatywne rozwiązanie, które jest jawnie rzucić zakaz arytmetycznych rodzajów arytmetyczną typy, które omija cały problem, że nie muszą już martwić się o kod staje źle sformułowane i jest przenośny:

std::isgreater(static_cast<double>(s) ,1.0) 
       ^^^^^^^^^^^^^^^^^^^^^^ 

jak Potatoswatter zaznacza wykorzystaniem pojedynczego + jest także opcja:

std::isgreater(+s ,1.0) 

Aktualizacja

jako TC wskazuje w C++ 11 można stwierdzić, że 26.8 ustęp 11 kula 3 obowiązuje od argumentu nie jest ani long double, podwójne ani liczbą całkowitą, a zatem powinno argumenty typu S powinny być oddane do pływaka pierwszy . Uwaga, jak wskazano w raporcie o usterkach gcc nigdy nie zaimplementowałem tego i o ile wiem, nie zrobiłem też clang.

+0

Prawdopodobnie C++ 11 sformułowanie wymaga 's' być oddane do' float', ponieważ jego typ jest ani '' długo double' ani double' ani typ całkowitą. –

+0

@ T.C.. Wciąż debatują, że jeden, ale tak to jest dyskusyjne. Uwaga, ani '' libC++ ani '' libstdC++ wdrożyć go w ten sposób. –

+0

'+ s' mogą być uznane za idiomatyczne. – Potatoswatter

Powiązane problemy