C++ 中文周刊 第63期

reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态

周刊项目地址在线地址知乎专栏 腾讯云+社区

欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue

5月20日周五下午8:48

最近“毕业”消息真多啊,搞的人心惶惶的。我要是被毕业了,这么多年没刷题,那可就完蛋了啊


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2022-05-18 第150期

文章

static_assert(-42 == -42z);
static_assert(-42 == -42Z);
static_assert(42 == 42uz);
static_assert(42 == 42uZ);
static_assert(42 == 42Uz);
static_assert(42 == 42ZU);
static_assert(42 == 42Zu);

static_assert(std::is_same_v<std::size_t, decltype(42uz)>);

没啥说的。别的语言都有的,早该加加了

比较函数很容易想到用tuple简化

// three-way comparison
int compare_3way_for_sorting(T const& a, T const& b)
{
    int a_key = key(a);
    ant b_key = key(b);

    if (a_key < b_key) return -1;
    if (a_key > b_key) return +1;
    return 0;
}

// less-than comparison
bool compare_less_for_sorting(T const& a, T const& b)
{
    return key(a) < key(b);
}

This reduces the problem to writing a sort key. Heres an example:

// Key generator: Sort by total cost (price + tax)
auto key(T const& t)
{
    return t.price + t.tax;
}

For a multi-level sort, you can return a std::pair or std::tuple with the most significant key as the first element.

// Key generator: Sort by length, then by width
auto key(T const& t)
{
    return std::make_pair(t.length, t.width);
}

// Key generator: Sort by length, then by width,
// then by weight
auto key(T const& t)
{
    return std::make_tuple(t.size, t.width, t.weight);
}

tuple的问题是值,拷贝太重,可能想到ref

auto key(T const& t)
{
    return std::make_tuple(std::ref(t.name), t.age);
}

或者用forward_as_tuple

auto key(T const& t)
{
    return std::forward_as_tuple(t.name, t.age);
}

问题在与forward_as_tuple拿的是引用,所以如果你传右值就完了,除非生命周期就一行

auto key(T const& t)
{
    return std::forward_as_tuple(t.name, t.height * t.width);
}

这样不行,第二个值在外面很有可能乱了

bool compare_less_for_sorting(T const& a, T const& b)
{
    return std::forward_as_tuple(a.name, a.height * a.width) <
     std::forward_as_tuple(b.name, b.height * b.width);
}

这样可以,生命周期就一行,比较完了就扔掉

一般来说,要传出去当个lambda,只好用tuple + ref

bool compare_less_for_sorting(T const& a, T const& b)
{
    auto key = [](auto&& t) {
        return std::make_tuple(std::ref(t.name), t.height * t.width);
    };
    return key(a) < key(b);
}


void f(std::vector<T>& v)
{
    auto key = [](auto&& t) {
        return std::make_tuple(std::ref(t.name), t.height * t.width);
    };
    std::sort(v.begin(), v.end(), [key](auto&& t, auto&& b) {
        return key(a) < key(b);
    });
}

当然,也可以不用tuple,一个字段一个字段来解

int compare_3way_for_sorting(T const& a, T const& b)
{
    // First compare by name
    if (a.name < b.name) return -1;
    if (a.name > b.name) return +1;

    // Names are equal, check connector names
    auto&& a_connector = a.GetConnector();
    auto&& b_connector = b.GetConnector();

    if (a_connector.name < b_connector.name) return -1;
    if (a_connector.name > b_connector.name) return +1;

    // Names and connector names are equal,
    // check manufacturing date
    auto&& a_date = LookupManufacturingDate(a.part_number);
    auto&& b_date = LookupManufacturingDate(b.part_number);

    if (a_date < b_date) return -1;
    if (a_date > b_date) return +1;

    // All keys match
    return 0;
}

// less-than comparison
bool compare_less_for_sorting(T const& a, T const& b)
{
    // First compare by name
    if (a.name < b.name) return true;
    if (a.name > b.name) return false;

    // Names are equal, check connector names
    auto&& a_connector = a.GetConnector();
    auto&& b_connector = b.GetConnector();

    if (a_connector.name < b_connector.name) return true;
    if (a_connector.name > b_connector.name) return false;

    // Names and connector names are equal,
    // check manufacturing date
    auto&& a_date = LookupManufacturingDate(a.part_number);
    auto&& b_date = LookupManufacturingDate(b.part_number);

    if (a_date < b_date) return true;
    if (a_date > b_date) return false;

    // All keys match
    return false;
}

用auto&&触发完美转发,肯定没损失

接着tuple方案进阶一下,如果我们想要定制比较,让比较的这个函数可变,且不传多个lambda

template<typename Lambda>
struct defer_comparison
{    
    defer_comparison(Lambda lambda) : key(std::move(lambda)){}
    Lambda key;

    auto operator<=>(defer_comparison const& other) const
        { return compare_3way(key(), other.key() ); }
};
 
auto key(T const& t)
{
    return std::make_tuple(std::ref(t.name),
                          defer_comparison([&] { return t.GetConnector(); }),
                          defer_comparison([&] { return LookupManufacturingDate(t.part_number); }));
}

std::weak_ordering
compare_3way_for_sorting(T const& a, T const& b)
{
    return key(a) <=> key(b);
}

bool compare_less_for_sorting(T const& a, T const& b)
{
    return key(a) < key(b);
}


bool compare_less_for_sorting(T const& a, T const& b)
{
    auto a_tuple = key(a);
    auto b_tuple = key(b);

    if (std::get<0>(a) < std::get<0>(b)) return true;
    if (std::get<0>(a) > std::get<0>(b)) return false;

    if (std::get<1>(a) < std::get<1>(b)) return true;
    if (std::get<1>(a) > std::get<1>(b)) return false;

    if (std::get<2>(a) < std::get<2>(b)) return true;
    if (std::get<2>(a) > std::get<2>(b)) return false;

    return false;
}

当然这种在c++20有更好的写法

// three-way comparison
std::weak_ordering
compare_3way_for_sorting(T const& a, T const& b)
{
    // First compare by name
    std::weak_ordering cmp = a.name <=> b.name;
    if (cmp != 0) return cmp;

    // Names are equal, check connector names
    cmp = a.GetConnector() <=> b.GetConnector();
    if (cmp != 0) return cmp;

    // Names and connector names are equal,
    // manufacturing date is the last check.
    cmp = LookupManufacturingDate(a.part_number) <=>
          LookupManufacturingDate(b.part_number);
    return cmp;
}

// less-than comparison
bool compare_less_for_sorting(T const& a, T const& b)
{
    // First compare by name
    std::weak_ordering cmp = a.name <=> b.name;
    if (cmp != 0) return cmp < 0;

    // Names are equal, check connector names
    cmp = a.GetConnector() <=> b.GetConnector();
    if (cmp != 0) return cmp < 0;

    // Names and connector names are equal,
    // manufacturing date is the last check.
    cmp = LookupManufacturingDate(a.part_number) <=>
          LookupManufacturingDate(b.part_number);
    return cmp < 0;
}

或者

// three-way comparison
std::weak_ordering
compare_3way_for_sorting(T const& a, T const& b)
{
    std::weak_ordering cmp = a.name <=> b.name;
    if (cmp == 0) cmp = a.GetConnector() <=> b.GetConnector();
    if (cmp == 0) cmp = LookupManufacturingDate(a.part_number) <=>
                        LookupManufacturingDate(b.part_number);
    return cmp;
}

// less-than comparison
bool compare_less_for_sorting(T const& a, T const& b)
{
    // First compare by name
    std::weak_ordering cmp = a.name <=> b.name;
    if (cmp == 0) cmp = a.GetConnector() <=> b.GetConnector();
    if (cmp == 0) cmp = LookupManufacturingDate(a.part_number) <=>
                        LookupManufacturingDate(b.part_number);
    return cmp < 0;
}

也可以有保底策略

std::weak_ordering
compare_3way_for_sorting(T const& a, T const& b)
{
    auto cmp = std::compare_weak_order_fallback(a.name, b.name);
    if (cmp == 0) cmp = std::compare_weak_order_fallback(a.GetConnector(), b.GetConnector());
    if (cmp == 0) cmp = std::compare_weak_order_fallback(LookupManufacturingDate(a.part_number),
                                                         LookupManufacturingDate(b.part_number));
    return cmp;
}

// less-than comparison
bool compare_less_for_sorting(T const& a, T const& b)
{
    auto cmp = std::compare_weak_order_fallback(a.name, b.name);
    if (cmp == 0) cmp = std::compare_weak_order_fallback(a.GetConnector(), b.GetConnector());
    if (cmp == 0) cmp = std::compare_weak_order_fallback(LookupManufacturingDate(a.part_number),
                                                         LookupManufacturingDate(b.part_number));
    return cmp < 0;
}

如何同时保存lvalue和rvalue,你可能会想到variant,但是 variant是不支持存引用的,所以只能猥琐路线

template<typename T>
struct NonConstReference
{
    T& value_;
    explicit NonConstReference(T& value) : value_(value){};
};

template<typename T>
struct ConstReference
{
    T const& value_;
    explicit ConstReference(T const& value) : value_(value){};
};

template<typename T>
struct Value
{
    T value_;
    explicit Value(T&& value) : value_(std::move(value)) {}
};

template<typename T>
using Storage = std::variant<Value<T>, ConstReference<T>, NonConstReference<T>>;


template<typename... Functions>
struct overload : Functions...
{
    using Functions::operator()...;
    overload(Functions... functions) : Functions(functions)... {}
};

template<typename T>
T const& getConstReference(Storage<T> const& storage)
{
    return std::visit(
        overload(
            [](Value<T> const& value) -> T const&             { return value.value_; },
            [](NonConstReference<T> const& value) -> T const& { return value.value_; },
            [](ConstReference<T> const& value) -> T const&    { return value.value_; }
        ),
        storage
    );
}


class MyClass
{
public:
    explicit MyClass(std::string& value) :       storage_(NonConstReference(value)){}
    explicit MyClass(std::string const& value) : storage_(ConstReference(value)){}
    explicit MyClass(std::string&& value) :      storage_(Value(std::move(value))){}

    void print() const
    {
        std::cout << getConstReference(storage_) << '\n';
    }

private:
    Storage<std::string> storage_;
};

笑死。concept编译相关报错太离谱有人写提案建议标准优化这里

qt profile教程,减少编译时间

作者实现了个函数能检测构造函数支持,bigsix

#include <algorithm>
#include <iostream>
#include <type_traits>

template<typename T>
struct isBigSix: std::integral_constant<bool,
                                      std::is_default_constructible<T>::value &&
                                      std::is_copy_constructible<T>::value &&
                                      std::is_copy_assignable<T>::value &&
                                      std::is_move_constructible<T>::value &&
                                      std::is_move_assignable<T>::value &&
                                      std::is_destructible<T>::value>{};

当然,标准库已经有类似的东西了std::semiregular std::regular

这里作者讨论了一个问题,is_move_constructible并不表明这个类会move,也可能是copy 退化的

template<typename T>
concept BigSix = isBigSix<T>::value;

template <BigSix T>                                   
void swap(T& a, T& b) noexcept {
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

struct MyData{                                        
    MyData() = default;
    MyData(const MyData& ) {
        std::cout << "copy constructor\n";
    }
    MyData& operator=(const MyData& m) {
        std::cout << "copy assignment operator\n";
        return *this;
    }

};

int main(){
    MyData a, b;
    swap(a, b);       
    static_assert(BigSix<MyData>, "BigSix not supported");                             
}
//copy constructor
//copy assignment operator
//copy assignment operator

编译不会报错。调用swap的时候调用的是MyData的拷贝构造, 看cppreference文档

注解

无移动构造函数的但有接受 const T& 参数的复制构造函数的类型,满足 std::is_move_constructible

介绍range api rotate reverse replace remove 没啥说的

视频

介绍测试宏的。__cpp_lib_xx

Pure Virtual C++ 2022 Recordings Available

大部分都是工具相关,没啥说的

介绍VS的查找功能如何索引文件

image-20220520212229327

就给了个图,实现细节啥也没说

constexpr里不能用static_assert但能用assert,为啥?

她说了一大堆,我没怎么听懂,没字幕

开源项目需要人手

新项目介绍/版本更新

这个是国人项目

工作招聘

没有。都裁员呢


本文永久链接

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账