Zhonghui

每个不曾起舞的日子,都是对生命的辜负

User Tools

Site Tools


程序:cpp:高级技巧1

C++技巧之一

主要是模板、自动类型推断


// C++的特殊技巧:第一弹
// g++ --std=c++17 -Wall main.cpp
 
#include <algorithm>
#include <iostream>
#include <initializer_list>
 
// 1: 在初始化变量的时候,顺便做一些其他的操作
namespace init_operation {
 
// 主要用到的是「逗号表达式」,(a, b) == b
inline void test() {
    // Warning: 声明了一个变量但是没有使用
    // 因为有警告所以一般不会这么写
    // int _t((std::cout << "Hi" << std::endl, 0));
 
    // 演示,其实和上面的写法本质上是一样的,都是声明了一个新的变量
    // 逗号表达式的值是最后一部分的值
    int _t = (std::cout << "Hi" << std::endl, 0);
    std::cout << "_t = " << _t << std::endl;
 
    // 真正常用的写法
    // 前面的(void)避免编译器发出警告,像是「执行一个int的构造函数,但是把构造结果丢弃」
    (void)int((std::cout << "Hi Again" << std::endl, 0)); // 1 arg,初始化一个int
    // n args,常用于可变参数的情况
    (void)std::initializer_list<int>{ (std::cout << "Hi, init" << std::endl, 0) };
}
 
} // namespace init_operation
 
// 2: 特殊的运算符重载
namespace special_operator {
 
class F1 {
public:
    // 重载了一个有点奇怪的运算符:->*
    // 这个运算符是做什么的呢?看下面F2的演示
    // 再重复一遍:可重载的运算符是固定的(包括「->*」),自己是不能随机增加的
    template<typename Lambda>
    // 参数使用右值引用,是一个可调用的对象
    // 右值是为了支持「完美转发」 std::forward
    // 也可以同时高效接收左值和右值参数,不会多余地拷贝或移动
    void operator->*(Lambda&& l) const {
        l(1, 1);
    }
 
    // 在C++ 20中,operator重载也可以使用auto自动推断参数类型
    // C++ 17
    // template <typename Lambda>
    // void operator->*(Lambda&& lambda) const { ... }
    // C++ 20
    // void operator->*(auto lambda) const { ... }
};
 
class F2 {
public:
    // 一个普通的函数
    int func(float x) {
        return (int)(x + 0.5f);
    }
};
 
inline void test() {
    F1 f1;
    // 特殊写法,直接传递一个lambda表达式给操作符函数
    // (正常应该是函数调用的形式)
    f1->*[](int a, int b) {
        std::cout << a + b << std::endl;
    };
 
    F2 f2;
    // 类似普通的函数指针,成员函数也可以有指针
    int (F2::*fp)(float) = &F2::func;
    ((&f2)->*fp)(0.1f);
};
 
} // namespace special_operator
 
// 3: lambda表达式的自动参数类型推断(通用 lambda),C++ 14开始支持
namespace generic_lambda {
 
inline void test() {
    auto func = [](auto data) { // 使用auto自动推断参数类型,就像模板函数一样
        std::cout << data << std::endl;
    };
    func(1);
    func("hello");
}
 
// auto f = [](auto x) { return x + 1; };
// 底层会被编译为这样的结构
// struct Lambda {
//     template <typename T>
//     auto operator()(T x) const { return x + 1; }
// };
 
} // namespace generic_lambda
 
// 4: 参数类型、数量动态推导
namespace auto_args {
 
// 这是一个接受任意类型数量参数的模板
template<typename... Types>
struct MyStruct {
    void printTypes() {
        // 统计类型数量
        static constexpr size_t count = sizeof...(Types);
        (void)count;
 
        // 用initializer_list展开类型,打印每个类型的名字
        (void)std::initializer_list<int>{
            (std::cout << typeid(Types).name() << std::endl, 0)...
        };
    }
};
 
void print() {
    std::cout << "end" << std::endl;
}
 
// 递归打印每一个参数
template<typename T, typename... Args>
void print(T first, Args... rest) { // 多个未知类型、数量的参数
    std::cout << first << std::endl;
    print(rest...); // 直接传递
}
 
// 初始化列表技巧
// 可以参考上面的init_operation
template<typename... Args>
void printAll(Args... args) {
    int dummy[] = { (std::cout << args << "\n", 0)... };
    // 用逗号表达式保证依次执行每个 std::cout << args
    (void)dummy; // 避免编译器警告
}
 
inline void test() {
    // 可以这样用
    MyStruct<int, float, double> a;
    MyStruct<> b; // 甚至可以一个参数也不传
 
    a.printTypes(); // i f d
    // 关警告的技巧
    (void)b;
 
    // 用法
    print(1, 2.5, "hello", 'A'); // 会打印每个参数
    printAll(1, 2.5, "hello", 'A'); // 会打印每个参数
}
 
} // namespace auto_args
 
// 5: 自动推断返回值类型
namespace auto_return {
 
// 自动推断返回值类型,C++ 11就引入了,C++ 14增加了功能
// 写在非模板函数中其实没什么意义
// 当有多处return并且返回值类型不同的时候,编译器报错:无法自动推断唯一返回类型
auto func1() { return 42; }
 
// auto + 尾返回类型
// 这不是更麻烦了吗?
auto func2(int x, int y) -> int {
    return x + y;
}
 
// 用在这里就合理了
// 使用decltype描述返回值的类型
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}
 
inline void test() {
    auto a = func1();
    auto b = func2(1, 1);
    auto c = add(1.0, -1); // double
    (void)(a + b + c);
}
 
} // namespace auto_return
 
int main() {
    init_operation::test();
    special_operator::test();
    generic_lambda::test();
    auto_args::test();
    auto_return::test();
 
    return 0;
}
/var/www/DokuWikiStick/dokuwiki/data/pages/程序/cpp/高级技巧1.txt · Last modified: 2025/07/08 02:18 by zhonghui