使用“自动”类型推导-如何找出编译器推导的类型?


74

我如何找出使用auto关键字时编译器推断出的类型?

示例1:更简单

auto tickTime = 0.001;

是推导为afloat还是adouble?

示例2:更复杂(和我目前的头痛):

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

什么类型的nextTickTime

我遇到的问题是尝试发送nextTickTime给时std::cout。我收到以下错误:

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

4
如有疑问,我会作弊。制作一个廉价的黑客程序,该程序声明auto变量但不使用它,然后检查调试器认为它是什么。
user4581301

8
我使用eclipse IDE并且在大多数情况下,我只是将鼠标悬停在auto关键字上,然后弹出推断出的类型。
Galik'8

24
在任何IDE中都可以使用的最可靠的hack-请勿使用auto:)认真地讲,如果您真的担心推论出哪种类型,那么为什么要使用auto哪种类型又会在不同情况下导致不同类型呢?
勤奋的按键者

12
嗯...我想念什么?错误消息中就在那里?
Daniel Jour

2
关于auto tickTime = 0.001;没有f字面意思是双重的
-phuclv

Answers:


110

我喜欢使用Effective Modern C ++中的想法,该想法使用未实现的模板;输出的类型带有编译器错误:

 template<typename T> struct TD;

现在对于auto variable var,在其定义后添加:

 TD<decltype(var)> td;

并注意您的编译器的错误消息,其中将包含类型var


1
很好,很聪明!
马修·费舍尔

2
将一个template<typename T> void td(T t);用于自动变量var也工作?
棘轮怪胎

4
@ratchetfreak是的,但是您将得到链接器错误,而不是编译错误。补偿错误已在前面显示。
marcinj

33

一个不需要任何预先帮助者定义的lo-fi技巧是:

typename decltype(nextTickTime)::_

编译器会抱怨它_不是任何类型的成员nextTickTime


那挽救了我的一天!:)
Devolus '19

8

这是一个typeid用于boost::core::demangle在运行时获取类型名称的版本。

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

哪个在我的系统上打印此:

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

你能给出示例输出吗?我很好奇它是否与编译器错误消息中的消息相同,Boost会更聪明吗?
DouglasHeriot 2016年

5

typeid通常可以用来获取变量的类型。它取决于编译器,我已经看到它给出了奇怪的结果。g ++默认情况下启用RTTI,但不确定Windows端。

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

提到“ Windows方面”没有多大意义。GCC可在Windows上运行,其他流行的编译器也可运行,因此我假设您是指Microsoft的编译器。实际上,MSTI的默认情况下,RTTI也是打开的。您必须使用明确将其关闭/GR-bad_typeid如果尝试使用typeid进行编译时会遇到异常/GR-,因此问题将非常明显。(当然,默认情况下,任何C ++编译器都必须启用RTTI,否则,它完全违反了语言标准。)
Cody Gray

@CodyGray:编译器的默认设置不符合标准是很常见的。如果要使用符合标准的编译器,通常需要添加一些选项。
马丁·邦纳

我的经验表明,对于编译器的默认选项而言,放宽对标准的遵守相对比较普遍,但是禁用诸如RTTI或异常之类的基本语言功能似乎太过分了。我还没有见过一个普通的(即非嵌入式或其他特殊用例)编译器,可以立即禁用其中的任何一种。@martin
Cody Gray

此代码不使用RTTI。这些表达式的运行时类型在编译时是静态已知的,它们不是多态的。在禁用RTTI的情况下,它将编译并完美运行。
Oktalist,2016年

我在OS X下禁用了RTTI时出现编译失败。g ++ -std = c ++ 11 -g -fno-rtti junk.cpp junk.cpp:16:18:错误:无法将typeid与-fno-rtti std ::一起使用cout << typeid(tickTime).name()<< std :: endl;
马修·费舍尔

3

正如Daniel Jour所说,请阅读错误消息:

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

1
真正。在抛出编译器错误的情况下,可以在错误消息中找到该类型。但是,问题是针对代码成功编译且没有错误消息的情况。
kmiklas

2

低技术的解决方案是将鼠标悬停nextTickTime在某些图形用户界面给人的类型,否则设置一.nextTickTimecout,选择一个合理的期待值或功能。

通常,如果您知道什么类型,就可以使用;auto如果您不知道,请不要使用它。这有点直观。

因此,如果您知道它是一个插入器,则只需使用auto来减少咒语,如果结果是某种未知类型,则必须在使用之前先找出它的含义auto

另请参见Herb,Andrei和Scott的讨论auto


2
当涉及模板时,“如果您不知道不要使用它”是没有意义的。
Ben Voigt

2

这样的答案为打印类型名称提供了一个不错的功能(实际上是几个实现)。

此外,这个免费的,开源的,仅标头的库提供了一种打印s的值和类型的好方法chrono::duration

将这两个实用程序放在一起:

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

此输出对我来说:

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

2

错误消息中由编译器推断出的类型:

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

它是一个复杂的类型名称,但是在错误消息中却存在。


1

附带说明,要有效地打印出其中的值,nextTickTime应显式转换为合适的值std::chrono::duration并输出的结果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

1

这是一种强制执行编译错误的方法,它显示了以下类型tickTime

struct {} baD = tickTime;

0

@ jonathan-oconnor指出,您可以使用[[deprecated]]C ++ 14中引入的属性来产生非常干净的解决方案:

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

但是遗憾的是,MSVC发出的诊断未提及类型。(示例

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.