#include
using namespace std;
int main()
{
auto l = 4;
cout << l << endl;
return 0;
}
Какой полный тип установит компилятор для l?
[const] short|int|long [signed|unsigned]
Как это определить?
Ответ
Есть замечательный ответ от 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() << '
';
Для меня выводит:
i
и я предполагаю, что MSVC будет выводить:
int
Т.е. const пропал. Это происходит не из-за плохого качества реализации. Стандарт диктует такое поведение.
То, что я рекомендую далее, это:
template
которое может быть использовано так:
const int ci = 0;
std::cout << type_name
';
и для меня выводит:
int const
C++11 решение
Я использую __cxa_demangle для не-MSVC платформ для расшифровки типов. Но для MSVC я доверяюсь typeid здесь (не тестировалось). И эта сущность обёрнута вокруг некоторого простого теста, который определяет, восстанавливает и выводит cv-квалификаторы и ссылки для входного типа.
#include
template ::value)
r += " const";
if (std::is_volatile ::value)
r += " volatile";
if (std::is_lvalue_reference
Результаты
С помощью данного решения я могу сделать следующее:
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
';
std::cout << "decltype((i)) is " << type_name
';
std::cout << "decltype(ci) is " << type_name
';
std::cout << "decltype((ci)) is " << type_name
';
std::cout << "decltype(static_cast
';
std::cout << "decltype(static_cast
';
std::cout << "decltype(static_cast
';
std::cout << "decltype(foo_lref()) is " << type_name
';
std::cout << "decltype(foo_rref()) is " << type_name
';
std::cout << "decltype(foo_value()) is " << type_name
';
}
и вывод получается такой:
decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast
Расскажу (для примера) о разнице между 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
Т.е. каждая ссылка и cv-квалификатор срезаны.
C++14
Данный вариант решения обладает парой особенностей:
Он выполняется во время компиляции!
Компилятор сам делает работу вместо библиотеки (даже вместо стандартной библиотеки). Это означает более точные результаты для более свежих языковых фич (таких как лямбды).
#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* 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
Данный код будет автоматически заботиться о constexpr, если вы до сих пор застряли в древнем C++11. А если вы рисуете на стенах пещер с помощью C++98/03, то noexcept также будет принесён в жертву.
C++17
Новый стандартный класс std::string_view может заменить рукописный static_string
template
Комментариев нет:
Отправить комментарий