S SmartDocs
Serie: C++ cpp 406 righe · Aggiornato 2026-04-03

auto_decltype.cpp

C++/Part5_進階主題/Ch21_型別推導/auto_decltype.cpp

// auto_decltype.cpp
// 編譯指令:g++ -std=c++17 -Wall auto_decltype.cpp -o auto_decltype
//
// 本程式示範 auto 與 decltype 的型別推導規則

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>

// 輔助函式:取得可讀的型別名稱
template<typename T>
std::string type_name() {
    int status;
    char* demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
    std::string result = (status == 0) ? demangled : typeid(T).name();
    free(demangled);
    return result;
}

// ============================================================
// 第一部分:auto 基本用法
// ============================================================

void demo_auto_basics() {
    std::cout << "========================================\n";
    std::cout << "  auto 基本型別推導\n";
    std::cout << "========================================\n\n";

    auto i = 42;            // int
    auto d = 3.14;          // double
    auto f = 3.14f;         // float
    auto c = 'A';           // char
    auto b = true;          // bool
    auto s = "hello";       // const char*
    auto str = std::string("hello"); // std::string

    std::cout << "auto i = 42;            -> " << type_name<decltype(i)>() << "\n";
    std::cout << "auto d = 3.14;          -> " << type_name<decltype(d)>() << "\n";
    std::cout << "auto f = 3.14f;         -> " << type_name<decltype(f)>() << "\n";
    std::cout << "auto c = 'A';           -> " << type_name<decltype(c)>() << "\n";
    std::cout << "auto b = true;          -> " << type_name<decltype(b)>() << "\n";
    std::cout << "auto s = \"hello\";       -> " << type_name<decltype(s)>() << "\n";
    std::cout << "auto str = string(...); -> " << type_name<decltype(str)>() << "\n";
    std::cout << "\n";
}

// ============================================================
// 第二部分:auto 與 const 的交互
// ============================================================

void demo_auto_const() {
    std::cout << "========================================\n";
    std::cout << "  auto 與 const\n";
    std::cout << "========================================\n\n";

    const int ci = 100;
    auto a = ci;            // int — 忽略頂層 const
    const auto b = ci;      // const int — 手動加 const

    std::cout << "const int ci = 100;\n";
    std::cout << "auto a = ci;       -> " << type_name<decltype(a)>()
              << " (頂層 const 被忽略)\n";
    std::cout << "const auto b = ci; -> " << type_name<decltype(b)>()
              << " (手動保留 const)\n";

    a = 200;  // 合法,a 不是 const
    // b = 200;  // 編譯錯誤,b 是 const
    std::cout << "a 可以被修改: a = " << a << "\n";

    // 指標的情況:底層 const 會被保留
    const int* pc = &ci;
    auto c = pc;            // const int* — 底層 const 被保留

    std::cout << "\nconst int* pc = &ci;\n";
    std::cout << "auto c = pc;       -> " << type_name<decltype(c)>()
              << " (底層 const 保留)\n";
    std::cout << "\n";
}

// ============================================================
// 第三部分:auto 與參考
// ============================================================

void demo_auto_reference() {
    std::cout << "========================================\n";
    std::cout << "  auto 與參考\n";
    std::cout << "========================================\n\n";

    int x = 42;
    int& rx = x;

    auto a = rx;            // int — 忽略參考,a 是副本
    auto& b = rx;           // int& — 明確使用 auto&,b 參考 x
    const auto& c = rx;     // const int& — 唯讀參考

    std::cout << "int x = 42; int& rx = x;\n";
    std::cout << "auto a = rx;        -> " << type_name<decltype(a)>()
              << " (參考被忽略,a 是副本)\n";
    std::cout << "auto& b = rx;       -> " << type_name<decltype(b)>()
              << " (保留參考)\n";
    std::cout << "const auto& c = rx; -> " << type_name<decltype(c)>() << "\n\n";

    // 驗證:修改 a 不影響 x,修改 b 會影響 x
    a = 100;
    std::cout << "修改 a = 100 後: x = " << x << ", a = " << a << " (a 是副本)\n";

    b = 200;
    std::cout << "修改 b = 200 後: x = " << x << ", b = " << b << " (b 是參考)\n";
    std::cout << "\n";
}

// ============================================================
// 第四部分:auto&& — 轉發參考
// ============================================================

void demo_auto_forwarding_ref() {
    std::cout << "========================================\n";
    std::cout << "  auto&& 轉發參考\n";
    std::cout << "========================================\n\n";

    int x = 42;
    const int cx = 100;

    auto&& a = x;          // int& — x 是左值
    auto&& b = 42;         // int&& — 42 是右值
    auto&& c = cx;         // const int& — cx 是 const 左值
    auto&& d = std::move(x); // int&& — std::move(x) 是右值

    std::cout << "int x = 42; const int cx = 100;\n";
    std::cout << "auto&& a = x;           -> " << type_name<decltype(a)>() << "\n";
    std::cout << "auto&& b = 42;          -> " << type_name<decltype(b)>() << "\n";
    std::cout << "auto&& c = cx;          -> " << type_name<decltype(c)>() << "\n";
    std::cout << "auto&& d = std::move(x); -> " << type_name<decltype(d)>() << "\n";

    // auto&& 在 range-based for 中非常實用
    std::vector<std::string> words = {"hello", "world"};
    std::cout << "\n使用 auto&& 遍歷容器(避免不必要的複製):\n";
    for (auto&& word : words) {
        std::cout << "  " << word << " (型別: " << type_name<decltype(word)>() << ")\n";
    }
    std::cout << "\n";
}

// ============================================================
// 第五部分:auto 與迭代器
// ============================================================

void demo_auto_iterators() {
    std::cout << "========================================\n";
    std::cout << "  auto 與迭代器\n";
    std::cout << "========================================\n\n";

    std::vector<int> vec = {10, 20, 30, 40, 50};
    std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};

    // auto 大幅簡化迭代器的使用
    std::cout << "vector 迭代:\n";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << "  " << *it;
    }
    std::cout << "\n\n";

    std::cout << "map 迭代(不用 auto 會很冗長):\n";
    for (auto it = scores.begin(); it != scores.end(); ++it) {
        std::cout << "  " << it->first << ": " << it->second << "\n";
    }

    // C++17 結構化繫結更簡潔
    std::cout << "\nC++17 結構化繫結:\n";
    for (const auto& [name, score] : scores) {
        std::cout << "  " << name << ": " << score << "\n";
    }
    std::cout << "\n";
}

// ============================================================
// 第六部分:auto 與 lambda
// ============================================================

void demo_auto_lambda() {
    std::cout << "========================================\n";
    std::cout << "  auto 與 lambda\n";
    std::cout << "========================================\n\n";

    // lambda 的型別是匿名的,必須使用 auto
    auto add = [](int a, int b) { return a + b; };
    auto greet = [](const std::string& name) {
        return "你好, " + name + "!";
    };

    // 泛型 lambda(C++14)
    auto generic_add = [](auto a, auto b) { return a + b; };

    std::cout << "add(3, 4) = " << add(3, 4) << "\n";
    std::cout << greet("小明") << "\n";
    std::cout << "generic_add(1, 2) = " << generic_add(1, 2) << "\n";
    std::cout << "generic_add(1.5, 2.3) = " << generic_add(1.5, 2.3) << "\n";
    std::cout << "generic_add(string, string) = "
              << generic_add(std::string("Hello"), std::string(" World")) << "\n";
    std::cout << "\n";
}

// ============================================================
// 第七部分:auto 的陷阱
// ============================================================

void demo_auto_pitfalls() {
    std::cout << "========================================\n";
    std::cout << "  auto 的常見陷阱\n";
    std::cout << "========================================\n\n";

    // 陷阱 1:大括號初始化
    auto x1 = {1, 2, 3};  // std::initializer_list<int>
    // auto x2{1};         // C++17: int; C++11/14: initializer_list<int>
    std::cout << "auto x1 = {1,2,3}; -> " << type_name<decltype(x1)>() << "\n";

    // 陷阱 2:vector<bool> 的代理物件
    std::vector<bool> flags = {true, false, true};
    auto flag = flags[0];  // 不是 bool!是 vector<bool>::reference 代理物件
    std::cout << "auto flag = vector<bool>[0]; -> " << type_name<decltype(flag)>()
              << " (代理物件!)\n";
    bool real_flag = flags[0];  // 強制轉換為 bool
    std::cout << "bool real_flag = vector<bool>[0]; -> " << type_name<decltype(real_flag)>()
              << " (正確)\n";

    // 陷阱 3:不可見的隱式轉換
    std::vector<int> vec = {1, 2, 3};
    // unsigned int sz = vec.size();  // 可能的型別不匹配警告
    auto sz = vec.size();            // 正確取得 size_t
    std::cout << "auto sz = vec.size(); -> " << type_name<decltype(sz)>()
              << " (正確的 size_t)\n";
    std::cout << "\n";
}

// ============================================================
// 第八部分:decltype 基本用法
// ============================================================

void demo_decltype_basics() {
    std::cout << "========================================\n";
    std::cout << "  decltype 基本用法\n";
    std::cout << "========================================\n\n";

    int x = 42;
    const int cx = x;
    const int& rcx = x;
    int* px = &x;

    // decltype 保留所有修飾
    std::cout << "int x = 42;\n";
    std::cout << "decltype(x)   -> " << type_name<decltype(x)>() << "\n";
    std::cout << "decltype(cx)  -> " << type_name<decltype(cx)>() << " (保留 const)\n";
    std::cout << "decltype(rcx) -> " << type_name<decltype(rcx)>() << " (保留 const&)\n";
    std::cout << "decltype(px)  -> " << type_name<decltype(px)>() << "\n";

    std::cout << "\n";
}

// ============================================================
// 第九部分:decltype 對表達式的規則
// ============================================================

void demo_decltype_expressions() {
    std::cout << "========================================\n";
    std::cout << "  decltype 對表達式的規則\n";
    std::cout << "========================================\n\n";

    int x = 42;

    // 重要差異:變數名 vs 表達式
    std::cout << "int x = 42;\n";
    std::cout << "decltype(x)     -> " << type_name<decltype(x)>()
              << "  (變數名,直接取宣告型別)\n";
    std::cout << "decltype((x))   -> " << type_name<decltype((x))>()
              << " ((x) 是左值表達式,所以是 int&)\n";

    // 更多表達式範例
    int a = 1, b = 2;
    std::cout << "\ndecltype(a + b) -> " << type_name<decltype(a + b)>()
              << "  (加法結果是右值)\n";
    std::cout << "decltype(a += b) -> " << type_name<decltype(a += b)>()
              << " (複合賦值結果是左值)\n";

    // 條件表達式
    bool cond = true;
    std::cout << "decltype(cond ? a : b) -> " << type_name<decltype(cond ? a : b)>()
              << " (兩個左值,結果是左值)\n";

    std::cout << "\n";
}

// ============================================================
// 第十部分:decltype(auto)
// ============================================================

// 使用 decltype(auto) 完美保留回傳值型別
template<typename Container>
decltype(auto) access_element(Container& c, std::size_t index) {
    return c[index];  // 保留 operator[] 的回傳型別(通常是 T&)
}

// 對比:使用 auto(會失去參考語意)
template<typename Container>
auto access_element_auto(Container& c, std::size_t index) {
    return c[index];  // 回傳副本,失去參考!
}

void demo_decltype_auto() {
    std::cout << "========================================\n";
    std::cout << "  decltype(auto)\n";
    std::cout << "========================================\n\n";

    // 變數推導
    int x = 42;
    decltype(auto) a = x;     // int(等同 decltype(x))
    decltype(auto) b = (x);   // int&(等同 decltype((x)),小心!)

    std::cout << "int x = 42;\n";
    std::cout << "decltype(auto) a = x;   -> " << type_name<decltype(a)>() << "\n";
    std::cout << "decltype(auto) b = (x); -> " << type_name<decltype(b)>()
              << " (注意!加括號變成參考)\n\n";

    // 函式回傳值比較
    std::vector<int> vec = {10, 20, 30};

    // decltype(auto) 保留參考,可以修改原始容器
    decltype(auto) elem1 = access_element(vec, 0);
    elem1 = 999;
    std::cout << "使用 decltype(auto) 修改 vec[0]:\n";
    std::cout << "  vec[0] = " << vec[0] << " (成功修改為 999)\n";

    // auto 回傳副本,無法修改原始容器
    auto elem2 = access_element_auto(vec, 1);
    elem2 = 888;
    std::cout << "使用 auto 嘗試修改 vec[1]:\n";
    std::cout << "  vec[1] = " << vec[1] << " (仍然是 20,因為 auto 回傳副本)\n";
    std::cout << "  elem2 = " << elem2 << " (只修改了副本)\n";
    std::cout << "\n";
}

// ============================================================
// 第十一部分:auto vs decltype 總結比較
// ============================================================

void demo_comparison() {
    std::cout << "========================================\n";
    std::cout << "  auto vs decltype 比較總結\n";
    std::cout << "========================================\n\n";

    const int x = 42;
    const int& rx = x;

    std::cout << "const int x = 42; const int& rx = x;\n\n";
    std::cout << std::string(50, '-') << "\n";
    std::cout << "表達式              | auto          | decltype\n";
    std::cout << std::string(50, '-') << "\n";

    // auto 忽略頂層 const 和參考
    auto a1 = x;
    auto a2 = rx;
    std::cout << "= x                | " << type_name<decltype(a1)>()
              << "           | " << type_name<decltype(x)>() << "\n";
    std::cout << "= rx               | " << type_name<decltype(a2)>()
              << "           | " << type_name<decltype(rx)>() << "\n";

    auto& a3 = x;
    std::cout << "auto& / decl       | " << type_name<decltype(a3)>()
              << "    | (同上)\n";
    std::cout << std::string(50, '-') << "\n";

    std::cout << "\n關鍵差異:\n";
    std::cout << "  1. auto 像模板推導:忽略頂層 const 和參考\n";
    std::cout << "  2. decltype 忠實保留所有型別修飾\n";
    std::cout << "  3. decltype 對括號表達式會推導為參考\n";
    std::cout << "  4. decltype(auto) 結合兩者:auto 的便利 + decltype 的精確\n";
    std::cout << "\n";
}

// ============================================================
// 主程式
// ============================================================

int main() {
    std::cout << "╔══════════════════════════════════════╗\n";
    std::cout << "║  C++17 型別推導:auto 與 decltype     ║\n";
    std::cout << "╚══════════════════════════════════════╝\n\n";

    demo_auto_basics();
    demo_auto_const();
    demo_auto_reference();
    demo_auto_forwarding_ref();
    demo_auto_iterators();
    demo_auto_lambda();
    demo_auto_pitfalls();
    demo_decltype_basics();
    demo_decltype_expressions();
    demo_decltype_auto();
    demo_comparison();

    std::cout << "=== 程式結束 ===\n";
    return 0;
}

Articoli correlati