it-swarm-vi.tech

Có thể in một loại biến trong C ++ tiêu chuẩn không?

Ví dụ:

int a = 12;
cout << typeof(a) << endl;

Sản lượng dự kiến:

int
328
Jorge Ferreira

C++ 11 cập nhật một câu hỏi rất cũ: In loại biến trong C++.

Câu trả lời được chấp nhận (và tốt) là sử dụng typeid(a).name(), trong đó a là một tên biến.

Bây giờ trong C++ 11, chúng ta có decltype(x), có thể biến một biểu thức thành một loại. Và decltype() đi kèm với bộ quy tắc rất thú vị của riêng nó. Ví dụ: decltype(a)decltype((a)) nói chung sẽ là các loại khác nhau (và vì lý do chính đáng và dễ hiểu một khi những lý do đó được đưa ra).

Liệu typeid(a).name() đáng tin cậy của chúng ta có giúp chúng ta khám phá thế giới mới dũng cảm này không?

Không.

Nhưng công cụ đó sẽ không phức tạp. Và đó là công cụ mà tôi đang sử dụng như một câu trả lời cho câu hỏi này. Tôi sẽ so sánh và đối chiếu công cụ mới này với typeid(a).name(). Và công cụ mới này thực sự được xây dựng dựa trên typeid(a).name().

Vấn đề cơ bản:

typeid(a).name()

vứt bỏ vòng loại cv, tài liệu tham khảo và lvalue/rvalue-ness. Ví dụ:

const int ci = 0;
std::cout << typeid(ci).name() << '\n';

Đối với tôi đầu ra:

i

và tôi đoán về kết quả đầu ra của MSVC:

int

I E. const đã biến mất. Đây không phải là vấn đề QOI (Chất lượng thực hiện). Các tiêu chuẩn bắt buộc hành vi này.

Những gì tôi khuyên bạn dưới đây là:

template <typename T> std::string type_name();

sẽ được sử dụng như thế này:

const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';

và cho tôi kết quả đầu ra:

int const

<disclaimer> Tôi chưa thử nghiệm điều này trên MSVC. </disclaimer> Nhưng tôi hoan nghênh phản hồi từ những người làm.

Giải pháp C++ 11

Tôi đang sử dụng __cxa_demangle cho các nền tảng không phải MSVC theo khuyến nghị của ipapadop trong câu trả lời của anh ấy cho các loại demangle. Nhưng trên MSVC tôi đang tin tưởng typeid để gỡ tên (chưa được kiểm tra). Và lõi này được bao bọc xung quanh một số thử nghiệm đơn giản giúp phát hiện, khôi phục và báo cáo các vòng loại cv và tham chiếu đến loại đầu vào.

#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> 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<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

Kết quả

Với giải pháp này tôi có thể làm điều này:

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<decltype(i)>() << '\n';
    std::cout << "decltype((i)) is " << type_name<decltype((i))>() << '\n';
    std::cout << "decltype(ci) is " << type_name<decltype(ci)>() << '\n';
    std::cout << "decltype((ci)) is " << type_name<decltype((ci))>() << '\n';
    std::cout << "decltype(static_cast<int&>(i)) is " << type_name<decltype(static_cast<int&>(i))>() << '\n';
    std::cout << "decltype(static_cast<int&&>(i)) is " << type_name<decltype(static_cast<int&&>(i))>() << '\n';
    std::cout << "decltype(static_cast<int>(i)) is " << type_name<decltype(static_cast<int>(i))>() << '\n';
    std::cout << "decltype(foo_lref()) is " << type_name<decltype(foo_lref())>() << '\n';
    std::cout << "decltype(foo_rref()) is " << type_name<decltype(foo_rref())>() << '\n';
    std::cout << "decltype(foo_value()) is " << type_name<decltype(foo_value())>() << '\n';
}

và đầu ra là:

decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast<int&>(i)) is int&
decltype(static_cast<int&&>(i)) is int&&
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int&
decltype(foo_rref()) is int&&
decltype(foo_value()) is int

Lưu ý (ví dụ) sự khác biệt giữa decltype(i)decltype((i)). Cái trước là kiểu khai báo của i. Cái sau là "loại" của biểu thức i. (các biểu thức không bao giờ có loại tham chiếu, nhưng như một quy ước decltype đại diện cho các biểu thức giá trị với các tham chiếu lvalue).

Do đó, công cụ này là một phương tiện tuyệt vời chỉ để tìm hiểu về decltype, ngoài việc khám phá và gỡ lỗi mã của riêng bạn.

Ngược lại, nếu tôi xây dựng cái này chỉ trên typeid(a).name(), mà không thêm lại các vòng loại hoặc tham chiếu cv bị mất, đầu ra sẽ là:

decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast<int&>(i)) is int
decltype(static_cast<int&&>(i)) is int
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int

I E. Mọi tham chiếu và vòng loại cv đều bị loại bỏ.

Cập nhật C++ 14

Ngay khi bạn nghĩ rằng bạn đã có giải pháp cho một vấn đề đóng đinh, một người nào đó luôn thoát ra khỏi hư không và chỉ cho bạn một cách tốt hơn nhiều. :-)

Câu trả lời này from Jamboree chỉ ra cách lấy tên loại trong C++ 14 tại thời điểm biên dịch. Đó là một giải pháp tuyệt vời cho một vài lý do:

  1. Đó là thời gian biên dịch!
  2. Bạn nhận được trình biên dịch để thực hiện công việc thay vì thư viện (thậm chí là std :: lib). Điều này có nghĩa là kết quả chính xác hơn cho các tính năng ngôn ngữ mới nhất (như lambdas).

Jamboree'scâu trả lời không hoàn toàn sắp xếp mọi thứ cho VS, và tôi điều chỉnh mã của anh ấy một chút. Nhưng vì câu trả lời này nhận được rất nhiều lượt xem, hãy dành chút thời gian để đi đến đó và nêu lên câu trả lời của anh ấy, nếu không có nó, bản cập nhật này sẽ không bao giờ xảy ra.

#include <cstddef>
#include <stdexcept>
#include <cstring>
#include <ostream>

#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 <std::size_t N>
    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 <class T>
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
}

Mã này sẽ tự động sao lưu trên constexpr nếu bạn vẫn bị mắc kẹt trong C++ 11 cổ đại. Và nếu bạn đang vẽ trên tường hang bằng C++ 98/03, thì noexcept cũng bị hy sinh.

Cập nhật C++ 17

Trong các bình luận bên dưới Lyberta chỉ ra rằng std::string_view mới có thể thay thế static_string:

template <class T>
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() + 49, p.find(';', 49) - 49);
#  endif
#Elif defined(_MSC_VER)
    string_view p = __FUNCSIG__;
    return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}

Tôi đã cập nhật các hằng số cho VS nhờ vào công việc thám tử rất hay của Jive Dadson trong các bình luận bên dưới.

411
Howard Hinnant

Thử:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

Bạn có thể phải kích hoạt RTTI trong các tùy chọn trình biên dịch để điều này hoạt động. Ngoài ra, đầu ra của điều này phụ thuộc vào trình biên dịch. Nó có thể là một tên loại thô hoặc một biểu tượng xáo trộn tên hoặc bất cứ điều gì ở giữa.

221
Konrad Rudolph

Rất xấu nhưng thực hiện thủ thuật nếu bạn chỉ muốn biên dịch thông tin thời gian (ví dụ: để gỡ lỗi):

auto testVar = std::make_Tuple(1, 1.0, "abc");
decltype(testVar)::foo = 1;

Trả về:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:5:19: error: 'dummy_error' is not a member of 'std::Tuple<int, double, const char*>'
66
NickV

Đừng quên bao gồm <typeinfo>

Tôi tin rằng những gì bạn đang đề cập đến là nhận dạng loại thời gian chạy. Bạn có thể đạt được những điều trên bằng cách làm.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}
53
mdec

Lưu ý rằng các tên được tạo bởi tính năng RTTI của C++ là không xách tay. Ví dụ: lớp học

MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>

sẽ có các tên sau:

// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE

Vì vậy, bạn không thể sử dụng thông tin này để tuần tự hóa. Tuy nhiên, thuộc tính typeid (a) .name () vẫn có thể được sử dụng cho mục đích ghi nhật ký/gỡ lỗi

22
paercebal

Bạn có thể sử dụng các mẫu.

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }

Trong ví dụ trên, khi loại không khớp, nó sẽ in "không xác định".

18
Nick

Như đã đề cập, typeid().name() có thể trả về tên bị sai lệch. Trong GCC (và một số trình biên dịch khác), bạn có thể làm việc xung quanh nó với mã sau:

#include <cxxabi.h>
#include <iostream>
#include <typeinfo>
#include <cstdlib>

namespace some_namespace { namespace another_namespace {

  class my_class { };

} }

int main() {
  typedef some_namespace::another_namespace::my_class my_type;
  // mangled
  std::cout << typeid(my_type).name() << std::endl;

  // unmangled
  int status = 0;
  char* demangled = abi::__cxa_demangle(typeid(my_type).name(), 0, 0, &status);

  switch (status) {
    case -1: {
      // could not allocate memory
      std::cout << "Could not allocate memory" << std::endl;
      return -1;
    } break;
    case -2: {
      // invalid name under the C++ ABI mangling rules
      std::cout << "Invalid name" << std::endl;
      return -1;
    } break;
    case -3: {
      // invalid argument
      std::cout << "Invalid argument to demangle()" << std::endl;
      return -1;
    } break;
 }
 std::cout << demangled << std::endl;

 free(demangled);

 return 0;

}

14
ipapadop

Bạn có thể sử dụng một lớp đặc điểm cho việc này. Cái gì đó như:

#include <iostream>
using namespace std;

template <typename T> class type_name {
public:
    static const char *name;
};

#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)

DECLARE_TYPE_NAME(int);

int main()
{
    int a = 12;
    cout << GET_TYPE_NAME(a) << endl;
}

Định nghĩa DECLARE_TYPE_NAME tồn tại để giúp cuộc sống của bạn dễ dàng hơn khi khai báo lớp đặc điểm này cho tất cả các loại bạn cần.

Điều này có thể hữu ích hơn các giải pháp liên quan đến typeid vì bạn có thể kiểm soát đầu ra. Ví dụ: sử dụng typeid cho long long trên trình biên dịch của tôi sẽ cho "x".

10
Greg Hewgill

Trong C++ 11, chúng ta có dectype. Không có cách nào trong c ++ tiêu chuẩn để hiển thị loại biến chính xác được khai báo bằng cách sử dụng dectype. Chúng ta có thể sử dụng boost typeindex i.e type_id_with_cvr (cvr là viết tắt của const, volility, Reference) để in kiểu như bên dưới.

#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

int main() {
  int i = 0;
  const int ci = 0;
  cout << "decltype(i) is " << type_id_with_cvr<decltype(i)>().pretty_name() << '\n';
  cout << "decltype((i)) is " << type_id_with_cvr<decltype((i))>().pretty_name() << '\n';
  cout << "decltype(ci) is " << type_id_with_cvr<decltype(ci)>().pretty_name() << '\n';
  cout << "decltype((ci)) is " << type_id_with_cvr<decltype((ci))>().pretty_name() << '\n';
  cout << "decltype(std::move(i)) is " << type_id_with_cvr<decltype(std::move(i))>().pretty_name() << '\n';
  cout << "decltype(std::static_cast<int&&>(i)) is " << type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name() << '\n';
  return 0;
}
7
abodeofcode

Theo giải pháp của Howard , nếu bạn không muốn số ma thuật, tôi nghĩ đây là cách tốt để thể hiện và trông trực quan:

template <typename T>
constexpr auto type_name()
{
    std::string_view name, prefix, suffix;
#ifdef __clang__
    name = __PRETTY_FUNCTION__;
    prefix = "auto type_name() [T = ";
    suffix = "]";
#Elif defined(__GNUC__)
    name = __PRETTY_FUNCTION__;
    prefix = "constexpr auto type_name() [with T = ";
    suffix = "]";
#Elif defined(_MSC_VER)
    name = __FUNCSIG__;
    prefix = "auto __cdecl type_name<";
    suffix = ">(void)";
#endif
    name.remove_prefix(prefix.size());
    name.remove_suffix(suffix.size());
    return name;
}   
5
康桓瑋

Các câu trả lời khác liên quan đến RTTI (typeid) có thể là những gì bạn muốn, miễn là:

  • bạn có thể đủ khả năng chi phí bộ nhớ (có thể đáng kể với một số trình biên dịch)
  • tên lớp mà trình biên dịch của bạn trả về là hữu ích

Cách khác, (tương tự như câu trả lời của Greg Hewgill), là xây dựng bảng tính trạng thời gian biên dịch.

template <typename T> struct type_as_string;

// declare your Wibble type (probably with definition of Wibble)
template <>
struct type_as_string<Wibble>
{
    static const char* const value = "Wibble";
};

Xin lưu ý rằng nếu bạn bọc các khai báo trong một macro, bạn sẽ gặp khó khăn khi khai báo tên cho các loại mẫu lấy nhiều hơn một tham số (ví dụ: std :: map), do dấu phẩy.

Để truy cập tên của loại biến, tất cả những gì bạn cần là

template <typename T>
const char* get_type_as_string(const T&)
{
    return type_as_string<T>::value;
}
5
James Hopkin

Bạn cũng có thể sử dụng bộ lọc c ++ với tùy chọn -t (loại) để sắp xếp tên loại:

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

int main() {
  auto x = 1;
  string my_type = typeid(x).name();
  system(("echo " + my_type + " | c++filt -t").c_str());
  return 0;
}

Chỉ thử nghiệm trên linux.

4
Alan

Một giải pháp chung chung hơn mà không quá tải chức năng so với giải pháp trước đây của tôi:

template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";

    return Type;}

Ở đây MyClass là lớp do người dùng định nghĩa. Nhiều điều kiện có thể được thêm vào đây là tốt.

Thí dụ:

#include <iostream>



class MyClass{};


template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";
    return Type;}


int main(){;
    int a=0;
    std::string s="";
    MyClass my;
    std::cout<<TypeOf(a)<<std::endl;
    std::cout<<TypeOf(s)<<std::endl;
    std::cout<<TypeOf(my)<<std::endl;

    return 0;}

Đầu ra:

int
String
MyClass
4
Jahid

Tôi thích phương pháp của Nick, Một hình thức hoàn chỉnh có thể là thế này (cho tất cả các loại dữ liệu cơ bản):

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(short&) { return "short"; }
template<> const char* typeof(long&) { return "long"; }
template<> const char* typeof(unsigned&) { return "unsigned"; }
template<> const char* typeof(unsigned short&) { return "unsigned short"; }
template<> const char* typeof(unsigned long&) { return "unsigned long"; }
template<> const char* typeof(float&) { return "float"; }
template<> const char* typeof(double&) { return "double"; }
template<> const char* typeof(long double&) { return "long double"; }
template<> const char* typeof(std::string&) { return "String"; }
template<> const char* typeof(char&) { return "char"; }
template<> const char* typeof(signed char&) { return "signed char"; }
template<> const char* typeof(unsigned char&) { return "unsigned char"; }
template<> const char* typeof(char*&) { return "char*"; }
template<> const char* typeof(signed char*&) { return "signed char*"; }
template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
4
Jahid

Khi tôi thách thức, tôi quyết định kiểm tra xem người ta có thể đi xa đến đâu với thủ thuật mẫu độc lập với nền tảng (hy vọng).

Các tên được lắp ráp hoàn chỉnh tại thời gian biên dịch. (Điều đó có nghĩa là không thể sử dụng typeid(T).name(), do đó bạn phải cung cấp tên một cách rõ ràng cho các loại không phải là hợp chất.

Ví dụ sử dụng:

TYPE_NAME(int)
TYPE_NAME(void)
// You probably should list all primitive types here.

TYPE_NAME(std::string)

int main()
{
    // A simple case
    std::cout << type_name<void(*)(int)> << '\n';
    // -> `void (*)(int)`

    // Ugly mess case
    // Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
    std::cout << type_name<void (std::string::*(int[3],const int, void (*)(std::string)))(volatile int*const*)> << '\n';
    // -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`

    // A case with undefined types
    //  If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
    std::cout << type_name<std::ostream (*)(int, short)> << '\n';
    // -> `class? (*)(int,??)`
    // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
}

Mã số:

#include <type_traits>
#include <utility>

static constexpr std::size_t max_str_lit_len = 256;

template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N])
{
    if constexpr(I < N)
        return str[I];
    else
        return '\0';
}

constexpr std::size_t sl_len(const char *str)
{
    for (std::size_t i = 0; i < max_str_lit_len; i++)
        if (str[i] == '\0')
            return i;
    return 0;
}

template <char ...C> struct str_lit
{
    static constexpr char value[] {C..., '\0'};
    static constexpr int size = sl_len(value);

    template <typename F, typename ...P> struct concat_impl {using type = typename concat_impl<F>::type::template concat_impl<P...>::type;};
    template <char ...CC> struct concat_impl<str_lit<CC...>> {using type = str_lit<C..., CC...>;};
    template <typename ...P> using concat = typename concat_impl<P...>::type;
};

template <typename, const char *> struct trim_str_lit_impl;
template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S>
{
    using type = str_lit<S[I]...>;
};
template <std::size_t N, const char *S> using trim_str_lit = typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;

#define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit<STR_TO_VA(str)>::value>
#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)
#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)
#define STR_TO_VA_4(str,off) ::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)

template <char ...C> constexpr str_lit<C...> make_str_lit(str_lit<C...>) {return {};}
template <std::size_t N> constexpr auto make_str_lit(const char (&str)[N])
{
    return trim_str_lit<sl_len((const char (&)[N])str), str>{};
}

template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};
template <std::size_t A> struct cexpr_pow<A,0> {static constexpr std::size_t value = 1;};
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_str_lit_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>
{
    static constexpr auto func()
    {
        if constexpr (N >= cexpr_pow<10,X>::value)
            return num_to_str_lit_impl<N, X+1>::func();
        else
            return str_lit<(N / cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{};
    }
};
template <std::size_t N> using num_to_str_lit = decltype(num_to_str_lit_impl<N,1>::func());


using spa = str_lit<' '>;
using lpa = str_lit<'('>;
using rpa = str_lit<')'>;
using lbr = str_lit<'['>;
using rbr = str_lit<']'>;
using ast = str_lit<'*'>;
using amp = str_lit<'&'>;
using con = str_lit<'c','o','n','s','t'>;
using vol = str_lit<'v','o','l','a','t','i','l','e'>;
using con_vol = con::concat<spa, vol>;
using nsp = str_lit<':',':'>;
using com = str_lit<','>;
using unk = str_lit<'?','?'>;

using c_cla = str_lit<'c','l','a','s','s','?'>;
using c_uni = str_lit<'u','n','i','o','n','?'>;
using c_enu = str_lit<'e','n','u','m','?'>;

template <typename T> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>;
template <typename T> inline constexpr bool func_or_arr = std::is_function_v<T> || std::is_array_v<T>;

template <typename T> struct primitive_type_name {using value = unk;};

template <typename T, typename = std::enable_if_t<std::is_class_v<T>>> using enable_if_class = T;
template <typename T, typename = std::enable_if_t<std::is_union_v<T>>> using enable_if_union = T;
template <typename T, typename = std::enable_if_t<std::is_enum_v <T>>> using enable_if_enum  = T;
template <typename T> struct primitive_type_name<enable_if_class<T>> {using value = c_cla;};
template <typename T> struct primitive_type_name<enable_if_union<T>> {using value = c_uni;};
template <typename T> struct primitive_type_name<enable_if_enum <T>> {using value = c_enu;};

template <typename T> struct type_name_impl;

template <typename T> using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,
                                                                               typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,
                                            typename primitive_type_name<T>::value,
                                            typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;
template <typename T> inline constexpr const char *type_name = type_name_lit<T>::value;

template <typename T, typename = std::enable_if_t<!std::is_const_v<T> && !std::is_volatile_v<T>>> using enable_if_no_cv = T;

template <typename T> struct type_name_impl
{
    using l = typename primitive_type_name<T>::value::template concat<spa>;
    using r = str_lit<>;
};
template <typename T> struct type_name_impl<const T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con>,
                                 con::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<vol>,
                                 vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<const volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con_vol>,
                                 con_vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<T *>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, ast>,
                                 typename type_name_impl<T>::l::template concat<     ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &&>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp, amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T, typename C> struct type_name_impl<T C::*>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,
                                 typename type_name_impl<T>::l::template concat<     type_name_lit<C>, nsp, ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<enable_if_no_cv<T[]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<rbr, typename type_name_impl<T>::r>;
};
template <typename T, std::size_t N> struct type_name_impl<enable_if_no_cv<T[N]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<num_to_str_lit<N>, rbr, typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T()>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<rpa, typename type_name_impl<T>::r>;
};
template <typename T, typename P1, typename ...P> struct type_name_impl<T(P1, P...)>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<type_name_lit<P1>,
                          com::concat<type_name_lit<P>>..., rpa, typename type_name_impl<T>::r>;
};

#define TYPE_NAME(t) template <> struct primitive_type_name<t> {using value = STR_LIT(#t);};
3
HolyBlackCat
#include <iostream>
#include <typeinfo>
using namespace std;
#define show_type_name(_t) \
    system(("echo " + string(typeid(_t).name()) + " | c++filt -t").c_str())

int main() {
    auto a = {"one", "two", "three"};
    cout << "Type of a: " << typeid(a).name() << endl;
    cout << "Real type of a:\n";
    show_type_name(a);
    for (auto s : a) {
        if (string(s) == "one") {
            cout << "Type of s: " << typeid(s).name() << endl;
            cout << "Real type of s:\n";
            show_type_name(s);
        }
        cout << s << endl;
    }

    int i = 5;
    cout << "Type of i: " << typeid(i).name() << endl;
    cout << "Real type of i:\n";
    show_type_name(i);
    return 0;
}

Đầu ra:

Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int
1
Graywolf

Theo giải thích của Scott Meyers trong C++ hiện đại hiệu quả,

Các cuộc gọi đến std::type_info::name không được đảm bảo để trả về bất kỳ điều gì hợp lý.

Giải pháp tốt nhất là để cho trình biên dịch tạo ra một thông báo lỗi trong quá trình khấu trừ kiểu, ví dụ,

template<typename T>
class TD;

int main(){
    const int theAnswer = 32;
    auto x = theAnswer;
    auto y = &theAnswer;
    TD<decltype(x)> xType;
    TD<decltype(y)> yType;
    return 0;
}

Kết quả sẽ giống như thế này, tùy thuộc vào trình biên dịch,

test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;

test4.cpp:11:21: error: aggregate ‘TD<const int *> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;

Do đó, chúng ta biết rằng loại của xint, loại y là loại const int*

0
Milo Lu