Using 'auto' type deduction - how to find out what type the compiler deduced?

C++C++11AutoChrono

C++ Problem Overview


How can I find out what type the compiler deduced when using the auto keyword?

Example 1: Simpler

auto tickTime = 0.001;

Was this deduced as a float or a double?

Example 2: More complex (and my present headache):

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

What type is nextTickTime?

The problem I'm having is when I try to send nextTickTime to std::cout. I get the following error:

./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)

C++ Solutions


Solution 1 - C++

I like to use idea from Effective Modern C++ which uses non-implemented template; the type is output with compiler error:

 template<typename T> struct TD;

Now for auto variable var, after its definition add:

 TD<decltype(var)> td;

And watch error message for your compiler, it will contain type of var.

Solution 2 - C++

A lo-fi trick that doesn't require any prior helper definitions is:

typename decltype(nextTickTime)::_

The compiler will complain that _ isn't a member of whatever type nextTickTime is.

Solution 3 - C++

Here's a typeid version that uses boost::core::demangle to get the type name at runtime.

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

Which prints this on my 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> >

Solution 4 - C++

typeid can be used to get the type of variable most of the time. It is compiler dependent and I've seen it give strange results. g++ has RTTI on by default, not sure on the Windows side.

#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> > >

Solution 5 - C++

As Daniel Jour said, read the error message:

... _Tp = std::chrono::time_point<
           std::chrono::_V2::system_clock,
           std::chrono::duration<
             double, std::ratio<1l, 1000000000l> > > ...

Solution 6 - C++

A low tech solution is hover the mouse over nextTickTime which in some GUIs gives the type else set a . after nextTickTime in the cout and select a reasonable looking value or function.

In general if You know what type You get use auto if you don't know it don't use it. Which is a bit counter intuitive.

So if you know its a interator just use auto to reduce the incantations, if the result is some unknown type you have to find out what it is before using auto.

See also Herb, Andrei and Scott discussing auto

Solution 7 - C++

This SO answer gives a nice function for printing out the name of a type (actually a couple of implementations).

Additionally this free, open-source, header-only library gives a nice way to print out the value and type of chrono::durations.

Putting these two utilities together:

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

This output for me:

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

Solution 8 - C++

The type deduced by the compiler is in the error message:

/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 --------------------------------------------------------------------------------------->

It's a complicated type name but it is there in the error message.

Solution 9 - C++

As a side note, to effectively print out the value in nextTickTime you should explicitly convert to a suitable std::chrono::duration and output the result of 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

Solution 10 - C++

Here is a way to force a compile error, which shows the type of tickTime:

struct {} baD = tickTime;

Solution 11 - C++

@jonathan-oconnor points out that you can use the [[deprecated]] attribute introduced in C++14 to produce a very clean solution:

template<typename T>
[[deprecated]] constexpr void printType(T const&) {}

Unfortunately though, the diagnostic emitted by MSVC doesn't mention the type. (example)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkmiklasView Question on Stackoverflow
Solution 1 - C++marcinjView Answer on Stackoverflow
Solution 2 - C++John McFarlaneView Answer on Stackoverflow
Solution 3 - C++RamblingMadView Answer on Stackoverflow
Solution 4 - C++Matthew FisherView Answer on Stackoverflow
Solution 5 - C++Jacob KrallView Answer on Stackoverflow
Solution 6 - C++SurtView Answer on Stackoverflow
Solution 7 - C++Howard HinnantView Answer on Stackoverflow
Solution 8 - C++R SahuView Answer on Stackoverflow
Solution 9 - C++obatakuView Answer on Stackoverflow
Solution 10 - C++ptsView Answer on Stackoverflow
Solution 11 - C++John McFarlaneView Answer on Stackoverflow