Страницы

Поиск по вопросам

среда, 18 декабря 2019 г.

Определение полного типа, назначаемого компилятором через auto

#cpp #cpp11


#include 

using namespace std;

int main()
{
    auto l = 4;
    cout << l << endl;
    return 0;
}


Какой полный тип установит компилятор для l? 

[const] short|int|long [signed|unsigned]


Как это определить?
    


Ответы

Ответ 1



Есть замечательный ответ от Howard Hinnant на этот счёт на enSO, и я решил здесь предоставить его перевод с некоторыми упрощениями. Для начала стоит сказать, что вариант использования typeid(a).name(), где a имя переменной, достаточно хорош. Но в C++11 появился decltype(x), который превращает выражение в тип. И decltype() идёт вместе со своим набором интересных правил. Например, decltype(a) и decltype((a)) будут обычно давать различные типы. Может ли наш надёжный typeid(a).name() помочь нам исследовать этот прекрасный новый мир? Нет. Но инструмент, который может, не так уж и сложен. И этот инструмент я использую в качестве ответа на этот вопрос. Я сравню и покажу различия между этим новым инструментом и typeid(a).name(). И этот новый инструмент фактически построен поверх typeid(a).name(). Фундаментальные проблемы: typeid(a).name() отбрасывает cv-квалификаторы, ссылки, и lvalue/rvalue-шность. Например: const int ci = 0; std::cout << typeid(ci).name() << '\n'; Для меня выводит: i и я предполагаю, что MSVC будет выводить: int Т.е. const пропал. Это происходит не из-за плохого качества реализации. Стандарт диктует такое поведение. То, что я рекомендую далее, это: template std::string type_name(); которое может быть использовано так: const int ci = 0; std::cout << type_name() << '\n'; и для меня выводит: int const C++11 решение Я использую __cxa_demangle для не-MSVC платформ для расшифровки типов. Но для MSVC я доверяюсь typeid здесь (не тестировалось). И эта сущность обёрнута вокруг некоторого простого теста, который определяет, восстанавливает и выводит cv-квалификаторы и ссылки для входного типа. #include #include #ifndef _MSC_VER # include #endif #include #include #include template std::string type_name() { typedef typename std::remove_reference::type TR; std::unique_ptr own ( #ifndef _MSC_VER abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), #else nullptr, #endif std::free ); std::string r = own != nullptr ? own.get() : typeid(TR).name(); if (std::is_const::value) r += " const"; if (std::is_volatile::value) r += " volatile"; if (std::is_lvalue_reference::value) r += "&"; else if (std::is_rvalue_reference::value) r += "&&"; return r; } Результаты С помощью данного решения я могу сделать следующее: int& foo_lref(); int&& foo_rref(); int foo_value(); int main() { int i = 0; const int ci = 0; std::cout << "decltype(i) is " << type_name() << '\n'; std::cout << "decltype((i)) is " << type_name() << '\n'; std::cout << "decltype(ci) is " << type_name() << '\n'; std::cout << "decltype((ci)) is " << type_name() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(foo_lref()) is " << type_name() << '\n'; std::cout << "decltype(foo_rref()) is " << type_name() << '\n'; std::cout << "decltype(foo_value()) is " << type_name() << '\n'; } и вывод получается такой: decltype(i) is int decltype((i)) is int& decltype(ci) is int const decltype((ci)) is int const& decltype(static_cast(i)) is int& decltype(static_cast(i)) is int&& decltype(static_cast(i)) is int decltype(foo_lref()) is int& decltype(foo_rref()) is int&& decltype(foo_value()) is int Расскажу (для примера) о разнице между decltype(i) и decltype((i)). Первое - это тип объявления для i. Последнее - это "тип" выражения i. (выражения никогда не имеют ссылочный тип, но по соглашению decltype представляет lvalue выражения с lvalue ссылками). Таким образом, данный инструмент превосходное средство для изучения decltype, в добавок к исследованию и отладке вашего собственного кода. Для сравнения, если я соберу это на базе typeid(a).name(), без добавления потерянных cv-квалификаторов или ссылок, результат будет такой: decltype(i) is int decltype((i)) is int decltype(ci) is int decltype((ci)) is int decltype(static_cast(i)) is int decltype(static_cast(i)) is int decltype(static_cast(i)) is int decltype(foo_lref()) is int decltype(foo_rref()) is int decltype(foo_value()) is int Т.е. каждая ссылка и cv-квалификатор срезаны. C++14 Данный вариант решения обладает парой особенностей: Он выполняется во время компиляции! Компилятор сам делает работу вместо библиотеки (даже вместо стандартной библиотеки). Это означает более точные результаты для более свежих языковых фич (таких как лямбды). #include #include #include #include #ifndef _MSC_VER # if __cplusplus < 201103 # define CONSTEXPR11_TN # define CONSTEXPR14_TN # define NOEXCEPT_TN # elif __cplusplus < 201402 # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN # define NOEXCEPT_TN noexcept # else # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN constexpr # define NOEXCEPT_TN noexcept # endif #else // _MSC_VER # if _MSC_VER < 1900 # define CONSTEXPR11_TN # define CONSTEXPR14_TN # define NOEXCEPT_TN # elif _MSC_VER < 2000 # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN # define NOEXCEPT_TN noexcept # else # define CONSTEXPR11_TN constexpr # define CONSTEXPR14_TN constexpr # define NOEXCEPT_TN noexcept # endif #endif // _MSC_VER class static_string { const char* const p_; const std::size_t sz_; public: typedef const char* const_iterator; template CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN : p_(a) , sz_(N-1) {} CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN : p_(p) , sz_(N) {} CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;} CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;} CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;} CONSTEXPR11_TN const_iterator end() const NOEXCEPT_TN {return p_ + sz_;} CONSTEXPR11_TN char operator[](std::size_t n) const { return n < sz_ ? p_[n] : throw std::out_of_range("static_string"); } }; inline std::ostream& operator<<(std::ostream& os, static_string const& s) { return os.write(s.data(), s.size()); } template CONSTEXPR14_TN static_string type_name() { #ifdef __clang__ static_string p = __PRETTY_FUNCTION__; return static_string(p.data() + 31, p.size() - 31 - 1); #elif defined(__GNUC__) static_string p = __PRETTY_FUNCTION__; # if __cplusplus < 201402 return static_string(p.data() + 36, p.size() - 36 - 1); # else return static_string(p.data() + 46, p.size() - 46 - 1); # endif #elif defined(_MSC_VER) static_string p = __FUNCSIG__; return static_string(p.data() + 38, p.size() - 38 - 7); #endif } Данный код будет автоматически заботиться о constexpr, если вы до сих пор застряли в древнем C++11. А если вы рисуете на стенах пещер с помощью C++98/03, то noexcept также будет принесён в жертву. C++17 Новый стандартный класс std::string_view может заменить рукописный static_string: template constexpr std::string_view type_name() { using namespace std; #ifdef __clang__ string_view p = __PRETTY_FUNCTION__; return string_view(p.data() + 34, p.size() - 34 - 1); #elif defined(__GNUC__) string_view p = __PRETTY_FUNCTION__; # if __cplusplus < 201402 return string_view(p.data() + 36, p.size() - 36 - 1); # else return string_view(p.data() + 46, p.size() - 46 - 1); # endif #elif defined(_MSC_VER) string_view p = __FUNCSIG__; return string_view(p.data() + 38, p.size() - 38 - 7); #endif }

Ответ 2



Для вывода типа переменной во время компиляции можно воспользоваться следующим трюком template class diag; int main() { auto l = 4; auto& m = l; diag ltype; diag mtype; return 0; } Результат компилирования error C2079: 'ltype' uses undefined class 'diag' error C2079: 'mtype' uses undefined class 'diag' То есть тип l - int, m - int&

Ответ 3



4 - это целочисленный литерал типа int, такого же типа будет и l.

Комментариев нет:

Отправить комментарий