Warum ist isnan unklar, und wie es zu vermeiden?
Seit isnan
kann entweder ein makro (in C++98) oder eine Funktion im Namensraum std
(in C++11), ist eine offensichtliche (und möglicherweise naive) Möglichkeit, den code zu schreiben, der funktioniert in beiden Fällen wird illustriert durch dieses einfache Beispiel
#include <cmath>
int main() {
double x = 0;
using namespace std;
isnan(x);
}
Jedoch, kompilieren es gibt Fehler sowohl in GCC (mit -std=c++11) und Clang:
test.cc: In function ‘int main()’:
test.cc:6:10: error: call of overloaded ‘isnan(double&)’ is ambiguous
isnan(x);
^
test.cc:6:10: note: candidates are:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from test.cc:1:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:234:1: note: int isnan(double)
__MATHDECL_1 (int,isnan,, (_Mdouble_ __value)) __attribute__ ((__const__));
^
In file included from test.cc:1:0:
/usr/include/c++/4.8/cmath:626:3: note: constexpr bool std::isnan(long double)
isnan(long double __x)
^
/usr/include/c++/4.8/cmath:622:3: note: constexpr bool std::isnan(double)
isnan(double __x)
^
/usr/include/c++/4.8/cmath:618:3: note: constexpr bool std::isnan(float)
isnan(float __x)
^
Warum ist das zweideutig in C++11-und wie es funktioniert mit C++98 und C++11 vorzugsweise ohne zu viel bedingte Kompilierung?
- Welche Art ist
x
? x
istdouble
- Sicher. Ich habe das komplette kompilierbare Beispiel
- Wenn Sie weiterhin die Fehlermeldung erhalten, indem das schreiben
(isnan)(x);
dann ist es nichts mit Makros - Ich habe nicht die Fehler, die in g++ 4.9.2 für Windows, aber bekommen es auf godbolt, die verwendet eine Art Linux. Also vielleicht ist es ein bug in der glibc-Header
- auch etwas von einem bug, dass
std::isnan(long double)
undstd::isnan(float)
sind aufgeführt, als könnten Sie nie ausgewählt werden, wie auch immer; die Mehrdeutigkeit ist zwischen::isnan(double)
undstd::isnan(double)
- Schöne Beobachtungen. Ich Frage mich, ob es eine Möglichkeit gibt zu unterscheiden zwischen
std::isnan
und::isnan
irgendwie. - Ist es die nicht-standard-Attribut, das stört, oder die Zweideutigkeit zwischen
::isnan(double)
undstd::isnan(double)
? - guter Punkt, der eigentliche Fehler ist, dass es zwei verschiedene Funktionen. Der C++ Standard verlangt, dass, wenn
::isnan
vorhanden ist, dannstd::isnan
muss die gleiche Funktion injiziert namespace std. Die C++ - Bibliothek sollte mittels der C-Bibliothekisnan(double)
hinzufügen und nurstd::isnan(long double)
undstd::isnan(float)
. - Ich denke
constexpr
hier ist ein bug zu; C++14 [constexpr.Funktionen]/1 sagt, "Eine Umsetzung ist nicht deklarieren der standard-Bibliothek-Funktion der Unterschrift als constexpr, außer denen, wo es explizit gewünscht ist." Der standard legt NICHT fest, dassisnan
sollteconstexpr
. - cmath Funktionen constexpr ist ein bekanntes libstdc++ - Problem.
- es gibt bereits einen bug-report, ich habe es in meiner Antwort.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist ein
libstdc++
Fehler dokumentiert in dem Bericht std Funktionen Konflikte mit C-Funktionen als Gebäude mit " c++0x support (und using namespace std) mit einer Reproduktion Probe sehr ähnlich zu den OP ' s:und einer der Kommentare sagt:
Kann dies irgendwann behoben, bis dann die Lösung aus dem Bugreport ist wie folgt:
Mit
::isnan
soweit ich das sagen kann, funktioniert vor C++11 und in C++11.Natürlich ist das ein
libstdc++
spezifische Lösung, es sieht gültigenlibc++
als gut, aber wenn Sie brauchen, um Unterstützung für einen compiler, wo dies nicht funktioniert, müssen Sie wahrscheinlich zu greifen, um mit#if/#else
.Hinweis, wie angegeben von M. M mit
isnan
gekennzeichnet constexpr ist nicht konform, das ist ein bekanntes Problem, wie auch obwohl es keinen Beitrag zu diesem Thema.Auch Verwandte bug-reports: [C++11] call of overloaded 'isnan' ist mehrdeutig und Erkennen gelieferten mit Rückgabetyp bool. Der zweite beschreibt mögliche
libstdc++
Lösungen.Update
Wenn Sie möchten, eine gcc/clang Lösung sieht es aus wie Sie beide unterstützen
__builtin_isnan
finden Sie gcc-docs auf gelieferten für weitere Informationen. Siehe auch diesen glibc bug-report auf Ersatz isnan et al mit dem builtin.::isnan(double)
undstd::isnan(double)
. Der Kommentar scheint zu sprechen über die (nicht-) Mehrdeutigkeit zwischen::foo
undstd::foo
in dem Fall, wo::foo
's name wurde injiziertstd
durch die Header (das ist ausdrücklich erlaubt, da der C++11).Denken, nachdem dieser für eine Weile, ich glaube nicht, dass es eine portable Möglichkeit, dies zu tun vor C++11 überhaupt. Die C
isnan
makro wurde in C99, C++98 und C++03 basieren auf C89. Also, wenn Sie verlassen sich auf Ihre C++98/03 Umsetzung zu ziehen in eine C99-header, bietetisnan
(die non-konformen, by the way) Sie sind nicht-portable Annahmen sowieso.Austausch der
unsing
Richtlinieusing
Erklärung gibt Ihnen dann den folgenden code, portable C++11 (auch arbeiten mit libstdc++'s defekt) und möglicherweise für frühere Implementierungen, mit beiden Daumen. (Unabhängig davon, ob Sie bietenisnan
als ein makro oder eine Funktion in der globalennamespace
.)Wickeln diese in eine eigene Funktion zu machen scheint die
#if
akzeptabel.machen Sie Ihre eigenen:
return std::isnan(x)
isnan
im Spielreturn std::isnan(x)
können zu erweitern, um sich etwas gebrochen wiereturn std::((x) != (x));
(std::isnan)(x)
#define isnan __builtin_isnan
.Den Fehler deuten darauf hin, dass Sie eine isnan im globalen namespace, und ein anderer in den std-namespace. Das "using namespace std;" Ursachen Mehrdeutigkeit zwischen diesen.
Nicht übermäßig elegant, aber die folgenden könnten für Ihre Arbeit aus der angegebenen Anforderungen.
[ BEARBEITEN ] Der oben genannten gilt für den Teil der ursprünglichen Frage über die Vermeidung von Ambiguität zwischen einer makro isnan und die std::isnan. Wenn es einen 3. Konflikt ::isnan im globalen namespace dann die folgenden technisch abdecken, aber das ist noch hässlicher und zerbrechlicher.
[ EDIT #2 ] In der Antwort zu dem Kommentar über das "nicht compilieren auf C++98, die nicht über das makro definiert ist (es ist eine normale Funktion im globalen namespace), noch hat es isnan(double&) in den std-namespace"... so Etwas wie dieses funktionieren könnte in einer idealen Welt.
In der realen Welt jedoch Compiler haben unterschiedliche Regeln für die __cplusplus, die sind ziemlich inkonsistent. Für eine allgemeinere Diskussion und Antworten werde ich aufschieben zu wie erstelle ich eine portable isnan/Funktion isinf.
isnan
makro in Glibc.isnan(double&)
im std-namespace.