Verwenden des automatischen Typabzugs - Wie kann man herausfinden, welchen Typ der Compiler abgeleitet hat?

74

Wie kann ich herausfinden, welchen Typ der Compiler bei Verwendung des autoSchlüsselworts abgeleitet hat?

Beispiel 1: Einfacher

auto tickTime = 0.001;

Wurde dies als floatoder oder abgeleitet?double?

Beispiel 2: Komplexer (und meine gegenwärtigen Kopfschmerzen):

typedef std::ratio<1, 1> sec;
std::chrono::duration<double, sec > timePerTick2{0.001};
 auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;

Welcher Typ ist nextTickTime?

Das Problem , das ich habe, ist , wenn ich versuche zu senden nextTickTimean std::cout. Ich erhalte folgende Fehlermeldung:

./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:143:16: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
  std::cout << std::setprecision(12) << nextTickTime << std::endl; // time in seconds
            ^
In file included from /usr/include/c++/4.8.2/iostream:39:0,
             from ./main.cpp:10:
/usr/include/c++/4.8.2/ostream:602:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double, std::ratio<1l, 1000000000l> > >]’
 operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
kmiklas
quelle
4
Im Zweifelsfall betrüge ich. Erstellen Sie ein billiges Hack-Programm, das die automatische Variable deklariert, aber nicht verwendet, und überprüfen Sie dann, was der Debugger davon hält.
user4581301
8
Ich benutze eclipse IDEund die meiste Zeit bewege ich einfach die Maus über das autoSchlüsselwort und der abgeleitete Typ erscheint.
Galik
24
Der zuverlässigste Hack, der in jeder IDE funktioniert - Just Dont Use auto:) Im Ernst, wenn Sie sich wirklich Gedanken darüber machen, welcher Typ genau abgeleitet wurde, warum sollten Sie dann einen verwenden, autoder unter verschiedenen Umständen zu einem anderen Typ führen kann?
Fleißiger Schlüsselpresser
12
Ähm ... was vermisse ich? Ist es genau dort in der Fehlermeldung?
Daniel Jour
2
about auto tickTime = 0.001;: ohnef das wörtliche ist ein double
phuclv

Antworten:

110

Ich verwende gerne Ideen aus Effective Modern C ++, die nicht implementierte Vorlagen verwenden. Der Typ wird mit einem Compilerfehler ausgegeben:

 template<typename T> struct TD;

Fügen Sie nun für die automatische Variable varnach ihrer Definition Folgendes hinzu:

 TD<decltype(var)> td;

Und sehen Sie sich die Fehlermeldung für Ihren Compiler an, sie enthält den Typ var.

marcinj
quelle
1
nett, sehr schlau!
Matthew Fisher
2
würde a template<typename T> void td(T t);auch für die auto variable var funktionieren?
Ratschenfreak
4
@ratchetfreak ja, aber dann erhalten Sie einen Linker-Fehler und keinen Kompilierungsfehler. Kompatationsfehler werden früher angezeigt.
März
32

Ein Lo-Fi-Trick, für den keine vorherigen Hilfsdefinitionen erforderlich sind, ist:

typename decltype(nextTickTime)::_

Der Compiler wird sich beschweren, dass er _kein Mitglied eines beliebigen Typs nextTickTimeist.

John McFarlane
quelle
Das hat mir den Tag gerettet! :)
Devolus
8

Hier ist eine typeidVersion, mit boost::core::demangleder der Typname zur Laufzeit abgerufen wird.

#include <string>
#include <iostream>
#include <typeinfo>
#include <vector>
using namespace std::literals;

#include <boost/core/demangle.hpp>

template<typename T>
std::string type_str(){ return boost::core::demangle(typeid(T).name()); }

auto main() -> int{
    auto make_vector = [](auto head, auto ... tail) -> std::vector<decltype(head)>{
        return {head, tail...};
    };

    auto i = 1;
    auto f = 1.f;
    auto d = 1.0;
    auto s = "1.0"s;
    auto v = make_vector(1, 2, 3, 4, 5);

    std::cout
    << "typeof(i) = " << type_str<decltype(i)>() << '\n'
    << "typeof(f) = " << type_str<decltype(f)>() << '\n'
    << "typeof(d) = " << type_str<decltype(d)>() << '\n'
    << "typeof(s) = " << type_str<decltype(s)>() << '\n'
    << "typeof(v) = " << type_str<decltype(v)>() << '\n'
    << std::endl;
}

Welches druckt dies auf meinem System:

typeof(i) = int
typeof(f) = float
typeof(d) = double
typeof(s) = std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
typeof(v) = std::vector<int, std::allocator<int> >
Kaffee und Code
quelle
Können Sie eine Beispielausgabe geben? Ich bin gespannt, ob es das gleiche ist wie in den Compiler-Fehlermeldungen, davon macht Boost etwas klügeres?
DouglasHeriot
5

Mit typeid kann meistens der Variablentyp abgerufen werden. Es ist vom Compiler abhängig und ich habe gesehen, dass es seltsame Ergebnisse liefert. In g ++ ist RTTI standardmäßig aktiviert, auf der Windows-Seite nicht sicher.

#include <iostream>
#include <typeinfo>
#include <stdint.h>
#include <chrono>
#include <ctime>

typedef std::ratio<1, 1> sec;
int main()
{
    auto tickTime = .001;
    std::chrono::duration<double, sec > timePerTick2{0.001};
    auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;
    std::cout << typeid(tickTime).name() << std::endl;
    std::cout << typeid(nextTickTime).name() << std::endl;

    return 0;
}

./a.out | c++filt

double
std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > >
Matthew Fisher
quelle
Auf "die Windows-Seite" zu verweisen, macht wenig Sinn. GCC funktioniert unter Windows, ebenso wie andere beliebte Compiler. Ich gehe also davon aus, dass Sie sich auf den Compiler von Microsoft beziehen. Tatsächlich ist RTTI auch für MSVC standardmäßig aktiviert. Sie müssen es explizit mit deaktivieren /GR-. Sie erhalten eine bad_typeidAusnahme, wenn Sie versuchen, typeidbeim Kompilieren mit zu verwenden /GR-, sodass das Problem offensichtlich ist. (Natürlich müsste jeder C ++ - Compiler standardmäßig RTTI aktiviert haben, da dies sonst einen völligen Verstoß gegen den Sprachstandard darstellt.)
Cody Gray
@CodyGray: Es ist durchaus üblich, dass die Standardeinstellung eines Compilers nicht dem Standard entspricht. Wenn Sie einen standardkompatiblen Compiler wünschen, müssen Sie normalerweise einige Optionen hinzufügen.
Martin Bonner unterstützt Monica
Meine Erfahrung zeigt, dass es für die Standardoptionen eines Compilers relativ häufig ist, die Einhaltung von Standards zu lockern , aber das Deaktivieren grundlegender Sprachfunktionen wie RTTI oder Ausnahmen scheint viel zu weit. Ich habe keinen normalen (dh nicht eingebetteten oder anderen speziellen Anwendungsfall) Compiler gesehen, der eines dieser Dinge sofort deaktiviert. @martin
Cody Gray
Dieser Code verwendet kein RTTI. Die Laufzeittypen dieser Ausdrücke sind zur Kompilierungszeit statisch bekannt und nicht polymorph. Es wird kompiliert und läuft einwandfrei, wenn RTTI deaktiviert ist.
Oktalist
Ich erhalte einen Kompilierungsfehler mit deaktiviertem RTTI unter OS X. g ++ -std = c ++ 11 -g -fno-rtti junk.cpp junk.cpp: 16: 18: Fehler: Typ-ID kann nicht mit -fno-rtti std :: verwendet werden cout << typeid (tickTime) .name () << std :: endl;
Matthew Fisher
3

Wie Daniel Jour sagte, lesen Sie die Fehlermeldung:

... _Tp = std::chrono::time_point<
           std::chrono::_V2::system_clock,
           std::chrono::duration<
             double, std::ratio<1l, 1000000000l> > > ...
Jacob Krall
quelle
1
Wahr. Wenn ein Compilerfehler ausgelöst wird, finden Sie den Typ in der Fehlermeldung. Die Frage richtet sich jedoch an Fälle, in denen der Code erfolgreich kompiliert wurde und keine Fehlermeldung angezeigt wird.
kmiklas
2

Eine Low-Tech-Lösung ist das Bewegen des Mauszeigers, über nextTickTimeden in einigen GUIs der Typ "else .after" nextTickTimein "a" gesetzt wird, coutund ein angemessen aussehender Wert oder eine Funktion ausgewählt.

Wenn Sie wissen, welchen Typ Sie verwenden, autowenn Sie ihn nicht kennen , verwenden Sie ihn im Allgemeinen nicht. Welches ist ein bisschen kontraintuitiv.

Wenn Sie also wissen, dass es sich um einen Interator handelt, verwenden Sie einfach auto, um die Beschwörungsformeln zu reduzieren. Wenn das Ergebnis ein unbekannter Typ ist, müssen Sie vor der Verwendung herausfinden, um was es sich handelt auto.

Siehe auch Herb, Andrei und Scottauto

Surt
quelle
2
"Wenn Sie nicht wissen, verwenden Sie es nicht" macht keinen Sinn, wenn Vorlagen beteiligt sind.
Ben Voigt
2

Diese SO-Antwort bietet eine nette Funktion zum Ausdrucken des Namens eines Typs (tatsächlich einige Implementierungen).

Darüber hinaus bietet diese kostenlose Open-Source-Bibliothek nur für Header eine gute Möglichkeit, den Wert und den Typ von chrono::durations auszudrucken .

Zusammenfügen dieser beiden Dienstprogramme:

#include "chrono_io.h"
#include "type_name.h"
#include <iomanip>
#include <iostream>

int
main()
{
    using namespace date;
    typedef std::ratio<1, 1> sec;
    std::chrono::duration<double, sec > timePerTick2{0.001};
    auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;
    std::cout << type_name<decltype(nextTickTime)>() << '\n';
    std::cout << std::setprecision(12) << nextTickTime.time_since_epoch() << '\n';
}

Diese Ausgabe für mich:

std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<double, std::__1::ratio<1, 1000000000> > >
4.8530542088e+14ns
Howard Hinnant
quelle
2

Der vom Compiler abgeleitete Typ steht in der Fehlermeldung:

/usr/include/c++/4.8.2/ostream:602:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>;
 _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double, std::ratio<1l, 1000000000l> > >]’
  ^^   <-------- the long type name --------------------------------------------------------------------------------------->

Es ist ein komplizierter Typname, der jedoch in der Fehlermeldung enthalten ist.

R Sahu
quelle
1

Um den Wert in effektiv auszudrucken, nextTickTimesollten Sie explizit in einen geeigneten Wert konvertieren std::chrono::durationund das Ergebnis von ausgeben duration::count.

using std::chrono::duration_cast;
using std::chrono::seconds;

auto baseTime = ...;
std::cout << std::setprecision(12) << duration_cast<seconds>(nextTickTime - baseTime).count()
    << std::endl; // time in seconds
Obataku
quelle
1

Hier ist eine Möglichkeit, einen Kompilierungsfehler zu erzwingen, der den Typ von tickTime:

struct {} baD = tickTime;
pts
quelle