【现代C++学习】可变参数与type_traits的综合应用

参考来源:博客园-qicosmos
[参考来源:深入应用 C++ 11 代码优化与工程级应用-第3章]

一、可变参数

1.1 模板函数的展开

1.1.1 递归展开

// 使用std::enable_if_t 选择合适的重载函数
template <size_t I = 0, typename Tuple>
typename std::enable_if_t<I == std::tuple_size<Tuple>::value> PrintTp(Tuple t) {} // 相等时, 终止

template <size_t I = 0, typename Tuple>
typename std::enable_if_t<I < std::tuple_size<Tuple>::value> PrintTp(Tuple t)
{
	std::cout << std::get<I>(t) << "\n";
	PrintTp<I + 1>(t);
}

template <typename... Args>
void Print(Args... args)
{
	PrintTp(std::make_tuple(args...));
}

int main()
{
	Print("hello world!", 666);
	return 0;
}

1.1.2 逗号表达式和初始化列表展开

template <typename... Args>
void Print(Args... args)
{
	std::initializer_list<int> { ([&] {
		std::cout << args << "\n";
	}(), 0)...};
}

int main()
{
	Print("hello world!", 666);
	/*	展开为:
		std::initializer_list<int> {
			([&] { std::cout << "hello world!" << "\n";}(), 0),
			([&] { std::cout << 666 << "\n";}(), 0)
		}
		等价于: std::initializer_list<int> {0, 0};
	*/
	return 0;
}

1.2 模板类

可变参数模板类的参数包需要通过模板特化继承的方式展开

1.1.1 递归+特化

template <typename First, typename... Rest>
struct Sum { // 类的定义
	enum { value = Sum<First>::value + Sum<Rest...>::value };
};

template <typename Last>
struct Sum<Last> { // 递归终止类 
	enum { value = sizeof(Last) };
};
// 或者: template <> struct Sum { enum { value = 0}; };
template <typename First, typename... Rest>
struct Sum : std::integral_constant<int, 
    Sum<First>::value + Sum<Rest...>::value> {};

template <typename Last>
struct Sum<Last> : std::integral_constant<int, sizeof(Last)> {};

int main()
{
	std::cout << Sum<int, double, char>::size; // 13
	return 0;
}

1.3 消除重复代码

struct A { A(int) { puts("A(int)"); } };
struct B { B(int, double) { puts("B(int, double)"); } };

// 工厂函数--无需 typename T1, typename T2, ...
template <typename T, typename... Args>
T* Instance(Args&&... args)
{
	// 使用完美转发消除值拷贝的损耗
	return new T(std::forward<Args>(args)...);
}

int main()
{
	A* pa = Instance<A>(1);
	B* pb = Instance<B>(1, 2.);
	return 0;
}

二、获取可调用对象返回类型的 traits

#include <map>
#include <vector>
#include <iostream>
#include <algorithm>

class Person {
public:
    Person(int age, std::string name) : mAge(age), mName(std::move(name)) {}
    [[nodiscard]] int GetAge() const
    {
        return mAge;
    }
    [[nodiscard]] std::string GetName() const
    {
        return mName;
    }
private:
    int mAge;
    std::string mName;
};


template <typename Fn>
auto GroupByPost(const std::vector<Person>& vt, Fn&& keySelector) ->
std::multimap<decltype(keySelector(*static_cast<Person*>(nullptr))), Person>
{
    // 也可以前置:
    //      std::multimap<decltype(Fn(*((Person*)nullptr))), Person>
    //      std::multimap<decltype(Fn((Person&)nulltype)), Person> // 现在无法使用
    using KeyType = decltype(keySelector(*static_cast<Person*>(nullptr)));
    std::multimap<KeyType, Person> m;
    std::for_each(vt.begin(), vt.end(), [&] (const auto& person) {
        m.insert(std::make_pair(keySelector(person), person));
    });
    return m;
}

template <typename Fn>
std::multimap<std::result_of_t<Fn(Person)>, Person>
GroupBy(std::vector<Person>& vt, Fn&& keySelector)
{
    using KeyType = std::result_of_t<Fn(Person)>;
    std::multimap<KeyType, Person> m;
    std::for_each(vt.begin(), vt.end(), [&] (const auto& person) {
        m.insert(std::make_pair(keySelector(person), person));
    });
    return m;
}

template<typename R>
class Range {
public:
    using RValueType = typename R::value_type;
    Range() = default;
    ~Range() = default;

    // 实现容器内任意类型
    template <typename Container, typename KeyFn, typename ValueFn>
    std::multimap<std::result_of_t<KeyFn(RValueType)>, std::result_of_t<ValueFn(RValueType)>>
    GroupBy(Container& ct, KeyFn&& keySelector, ValueFn&& ValueSelector)
    {
        using KeyType = std::result_of_t<KeyFn(Person)>;
        using ValueType = std::result_of_t<ValueFn(Person)>;
        std::multimap<KeyType, ValueType> m;

        std::for_each(ct.begin(), ct.end(), [&] (const auto& person) {
            m.insert(std::make_pair(keySelector(person), ValueSelector(person)));
        });
        return m;
    }

    static Range<R>& GetInstance()
    {
        static Range<R> range;
        return range;
    }
};

template <typename T, size_t N>
void Print(T t)
{
    if constexpr (N <= 1) {
        std::cout << std::get<N - 1>(t);
        return;
    } else {
        Print<T, N - 1>(t);
        std::cout << ", " << std::get<N - 1>(t);
    }
}

template <typename... Args>
void Print(const std::tuple<Args...>& t)
{
    std::cout << "(";
    Print<decltype(t), sizeof...(Args)>(t);
    std::cout << ")";
}

int main()
{
	Person p1(10, "Bob");
    Person p2(10, "LiMei");
    Person p3(26, "Martin");
    std::vector vt{p1, p2, p3};

    auto gbp = GroupByPost(vt, [] (const auto& item) {
        return item.GetName();
    });
    std::for_each(gbp.begin(), gbp.end(), [] (const auto& item) {
        std::cout << item.first << "\t" << " age = "
            << item.second.GetAge() << " name = " << item.second.GetName() << "\n";
    });

    auto gb = GroupBy(vt, [] (const auto& item) {
        return item.GetAge();
    });
    std::for_each(gb.begin(), gb.end(), [] (const auto& item) {
        std::cout << item.first << "\t" << " age = "
            << item.second.GetAge() << " name = " << item.second.GetName() << "\n";
    });

    auto rgb = Range<decltype(vt)>::GetInstance().GroupBy(vt, [] (const Person& person) {
            // 不能使用 tie: connot bind non-const lvalue reference of type
            return std::make_tuple(person.GetName(), person.GetAge());
        }, [] (const Person& person) {
            return std::make_tuple(person.GetName()); 
        });
    std::for_each(rgb.begin(), rgb.end(), [] (const auto& item) {
        Print(item.first);
        std::cout << ":";
        Print(item.second);
        std::cout << "\n";
    });
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值