0
点赞
收藏
分享

微信扫一扫

简单又便宜的实现电脑远程开机唤醒方法

北冥有一鲲 2024-11-08 阅读 6

目录

std::span

结构化绑定

在 if 和 switch 语句中初始化变量

使用模板参数推导简化代码

更加高级的折叠表达式

扩展:MSVC下的Span实现

std::span

std::span!

这个东西是标准库在C++20新的STL中加入进来的。他更加像是一个已有数组资源的视图。它可以动态指定长度,也可以静态指定长度。这个东西跟笔者在《C++ STL CookBook 4》中谈到的Ranges View编程范式是一致的,span是一个view.

#include <iostream>
#include <span>
#include <array>
​
void do_check_int_span(const std::span<int>& my_span) {
    for (int i : my_span) {
        std::cout << i << " ";
    }
}
​
int main() {
    std::array<int, 5> my_array{1, 2, 3, 4, 5};
    std::span<int> my_span(my_array.begin(), my_array.end() - 2);
    do_check_int_span(my_span);
    std::cout << std::endl;
    return 0;
}

它的存在是有趣的,因为我们现在可以拿到任何一个连续的容器的span视图了。就跟笔者在上一篇博客谈到的那样。

Public Member Function返回值描述
T& front()序列中的第一个元素返回序列中的第一个元素
T& back()序列中的最后一个元素返回序列中的最后一个元素
T& operator[]指定索引位置的元素返回指定索引位置的元素
T* data()指向序列起始位置的指针返回指向序列起始位置的指针
iterator begin()指向第一个元素的迭代器返回指向第一个元素的迭代器
iterator end()指向最后一个元素后一个位置的迭代器返回指向最后一个元素后一个位置的迭代器
iterator rbegin()指向第一个元素的反向迭代器返回指向第一个元素的反向迭代器
iterator rend()指向最后一个元素后一个位置的反向迭代器返回指向最后一个元素后一个位置的反向迭代器
size_t size()序列中的元素个数返回序列中元素的数量
size_t size_bytes()序列所占字节数返回序列所占字节数
bool empty()如果为空,返回 true如果序列为空,返回 true
span<T> first(count)一个包含前 count 个元素的子 span返回一个包含前 count 个元素的子 span
span<T> last(count)一个包含后 count 个元素的子 span返回一个包含后 count 个元素的子 span
span<T> subspan(offset, count)一个从 offset 位置开始,包含 count 个元素的子 span返回一个从 offset 位置开始,包含 count 个元素的子 span

结构化绑定

我知道很多人都写过这样的代码

MyErrorPackage handle_some_process(const ParamsPackage& package)
{
    if( check_pramas_a( package ) )
    {
        return MyErrorPackage{ErrorCodeA, ErrorBufferStringA, AdviceA};
    }
    if( check_pramas_b( package ) )
    {
        return MyErrorPackage{ErrorCodeBErrorBufferStringB AdviceB)
    }
    ...
    handle_impl(package);
    return MyErrorPackage{NO_ERROR, "No ERROR", ""};
}
    
// at caller
MyErrorPackage package = handle_some_process(package);
auto code = package.code;
auto errorString = package.errorString;
auto adviceString = package.advice;
...

不得不承认实在是有点抽象,写的太累了。一个方便的办法是使用我们的结构化绑定,什么意思呢?对于平凡的数据结构类型。比如说一个经典的C Structure,就是一个POD

struct MyErrorPackage
{
    int code;
    std::string buffer;
    std::string advice;
};  // only contains data without method

很好,我们可以这样玩:

auto [code, buffer, advice] = handle_some_process(package);

一下我们就拿到了所有的成员。现在直接不用写那些取值代码吗,同时提升了性能。

ifswitch 语句中初始化变量

从 C++17 开始,ifswitch 语句现在具有初始化语法,类似于 C99 中的 for 循环。这使得你可以限制条件内使用的变量的作用域。也就是说,我们可以将那些没有必要泄漏到外面的,只是用在里面的if当中的变量放到我们的if - else结构当中:

const string artist{ "Jimi Hendrix" };
size_t pos{ artist.find("Jimi") };
if(pos != string::npos) {
    cout << "found\n";
} else {
    cout << "not found\n";
}

这种写法会将 pos 变量暴露到条件语句的外部,需要在其他地方管理,或者可能与其他使用同一标识符的代码发生冲突。现在,你可以将初始化表达式放在 if 语句条件中:

if(size_t pos{ artist.find("Jimi") }; pos != string::npos) {
    cout << "found\n";
} else {
    cout << "not found\n";
}

我们来看看,其实就是这个意思:

if(auto var{ init_value }; condition) {
    // var 在这里可见 
} else {
    // var 仍然在这里可见 
} 
// var 在这里不可见 

在这个例子中,初始化表达式定义的变量在整个 if 语句的范围内可见,包括 else 部分。一旦控制流退出 if 语句的作用域,变量将不再可见,并且相关的析构函数会被调用。

相同的,我们作用到switch语句里去,使用初始化表达式与 switch 语句:

switch(auto var{ init_value }; var) {
case 1: ...
case 2: ...
case 3: ...
...
default: ...
}
// var 在这里不可见 

switch 语句中,初始化表达式定义的变量在整个 switch 语句的作用域内可见,包括所有的 case 部分和 default 部分。如果控制流离开 switch 语句的作用域,变量将不再可见,并且相关的析构函数会被调用。

一个有趣的应用场景是限制 lock_guard(用于锁定互斥量)的作用域。这可以通过初始化表达式轻松实现:

if (lock_guard<mutex> lg{ my_mutex }; condition) { 
    // 这里会发生有趣的事情 
}

在构造函数中,lock_guard 锁定了互斥量,并且在析构函数中自动解锁。当 if 语句的作用域结束时,lock_guard 会自动销毁。在过去,你需要手动删除它或用额外的花括号将整个 if 语句包裹起来。

使用模板参数推导简化代码

模板参数推导发生在模板函数或类模板构造函数(从 C++17 开始)中,当参数的类型足够明确时,编译器可以自动推导出模板类型,而无需显式指定模板参数。这个特性有一些规则,但大多数情况下非常直观。

一般来说,模板参数推导在使用与模板参数兼容的参数时会自动发生。在函数模板中,参数推导通常像这样工作:

template<typename T>
const char * f(const T a) {
    return typeid(T).name();
}
​
int main() {
    cout << format("T is {}\n", f(47));
    cout << format("T is {}\n", f(47L));
    cout << format("T is {}\n", f(47.0));
    cout << format("T is {}\n", f("47"));
    cout << format("T is {}\n", f("47"s));
}
T is int
T is long
T is double
T is char const *
T is class std::basic_string<char...>

由于类型非常容易识别,因此无需在调用时显式指定模板参数,例如 f<int>(47)。编译器可以根据传入的参数推导出 <int> 类型。

对于多个模板参数,推导同样有效:

template<typename T1, typename T2>
string f(const T1 a, const T2 b) {
    return format("{} {}", typeid(T1).name(), typeid(T2).name());
}
​
int main() {
    cout << format("T1 T2: {}\n", f(47, 47L));
    cout << format("T1 T2: {}\n", f(47L, 47.0));
    cout << format("T1 T2: {}\n", f(47.0, "47"));
}
T1 T2: int long
T1 T2: long double
T1 T2: double char const *

在这个例子中,编译器推导出了 T1T2 的类型。

  • 注意,类型必须与模板兼容。例如,你不能对字面量类型取引用:

template<typename T>
const char * f(const T& a) {
    return typeid(T).name();
}
​
int main() {
    int x{47};
    f(47);  // 编译错误
    f(x);   // 这行编译通过
}
  • 从 C++17 开始,模板参数推导也适用于类。因此,现在可以这样写:

pair p(47, 47.0);      // 推导为 pair<int, double>
tuple t(9, 17, 2.5);   // 推导为 tuple<int, int, double>

这消除了使用 std::make_pair()std::make_tuple() 的需求,因为你现在可以直接初始化这些类,而不需要显式指定模板参数。为了兼容旧代码,std::make_* 辅助函数仍然可用。

更加高级的折叠表达式

考虑一个包含参数包的构造函数:

cpp复制代码template <typename T>
class Sum {
    T v{};
public:
    template <typename... Ts>
    Sum(Ts&& ... values) : v{ (values + ...) } {}
    const T& value() const { return v; }
};

构造函数中使用了一个折叠表达式(values + ...),这是 C++17 的特性,应用操作符到参数包中的所有成员。在此示例中,它将 v 初始化为参数包的和。

该类的构造函数接受任意数量的参数,每个参数可以是不同的类。例如,我可以这样调用:

Sum s1 { 1u, 2.0, 3, 4.0f };  // unsigned, double, int, float
Sum s2 { "abc"s, "def" };      // std::string, c-string

然而,这不会编译,因为模板参数推导无法为这些不同类型的参数找到一个共同的类型。错误信息大概是:

cannot deduce template arguments for 'Sum'

我们可以通过模板推导指南来解决这个问题。推导指南是一个帮助模式,辅助编译器进行复杂的推导。以下是针对我们的构造函数的指南:

template <typename... Ts>
Sum(Ts&& ... ts) -> Sum<std::common_type_t<Ts...>>;

这告诉编译器使用 std::common_type_t 特性,尝试为所有参数包中的类型找到一个共同的类型。现在,参数推导就会成功,并且我们可以查看它使用的类型:

Sum s1 { 1u, 2.0, 3, 4.0f };  // unsigned, double, int, float
Sum s2 { "abc"s, "def" };      // std::string, c-string
auto v1 = s1.value();
auto v2 = s2.value();
cout << format("s1 is {} {}, s2 is {} {}", typeid(v1).name(), v1, typeid(v2).name(), v2);

输出:

s1 is double 10, s2 is class std::string abcdef

扩展:MSVC下的Span实现

class span : private _Span_extent_type<_Ty, _Extent> {
private:
    using _Base = _Span_extent_type<_Ty, _Extent>;
    using _Base::_Mydata;   // 这个是T*
    using _Base::_Mysize;   // 这个是size
​
public:
    // 类型定义
    using element_type     = _Ty;
    using value_type       = remove_cv_t<_Ty>;
    using size_type        = size_t;
    using difference_type  = ptrdiff_t;
    using pointer          = _Ty*;
    using const_pointer    = const _Ty*;
    using reference        = _Ty&;
    using const_reference  = const _Ty&;
    using iterator         = _Span_iterator<_Ty>;
    using reverse_iterator = _STD reverse_iterator<iterator>;
​
#if _HAS_CXX23
    using const_iterator         = _STD const_iterator<iterator>;
    using const_reverse_iterator = _STD const_iterator<reverse_iterator>;
#endif
​
    static constexpr size_type extent = _Extent;
​
    // 构造函数
    constexpr span() noexcept 
        requires (_Extent == 0 || _Extent == dynamic_extent) = default;
​
    // 从迭代器构造
    template <_Span_compatible_iterator<element_type> _It>
    constexpr explicit(_Extent != dynamic_extent) span(_It first, size_type count) noexcept
        : _Base(_STD to_address(_STD _Get_unwrapped_n(first, count)), count) {
        #if _CONTAINER_DEBUG_LEVEL > 0
        if constexpr (_Extent != dynamic_extent) {
            _STL_VERIFY(count == _Extent, "无法构造具有静态范围的 span,范围大小不一致");
        }
        #endif
    }
​
    // 从范围构造
    template <_Span_compatible_iterator<element_type> _It, _Span_compatible_sentinel<_It> _Sentinel>
    constexpr explicit(_Extent != dynamic_extent) span(_It first, _Sentinel last) noexcept(noexcept(last - first))
        : _Base(_STD to_address(first), static_cast<size_type>(last - first)) {
        _STD _Adl_verify_range(first, last);
        #if _CONTAINER_DEBUG_LEVEL > 0
        if constexpr (_Extent != dynamic_extent) {
            _STL_VERIFY(last - first == _Extent, "无法从范围构造具有静态范围的 span");
        }
        #endif
    }
​
    // 从数组构造
    template <size_t _Size>
    requires (_Extent == dynamic_extent || _Extent == _Size)
    constexpr span(type_identity_t<element_type> (&arr)[_Size]) noexcept 
        : _Base(arr, _Size) {}
​
    // 从数组构造(非 const)
    template <class _OtherTy, size_t _Size>
    requires (_Extent == dynamic_extent || _Extent == _Size) && is_convertible_v<_OtherTy (*)[], element_type (*)[]>
    constexpr span(array<_OtherTy, _Size>& arr) noexcept 
        : _Base(arr.data(), _Size) {}
​
    // 从数组构造(const)
    template <class _OtherTy, size_t _Size>
    requires (_Extent == dynamic_extent || _Extent == _Size) && is_convertible_v<const _OtherTy (*)[], element_type (*)[]>
    constexpr span(const array<_OtherTy, _Size>& arr) noexcept 
        : _Base(arr.data(), _Size) {}
​
    // 从范围构造
    template <_Span_compatible_range<element_type> _Rng>
    constexpr explicit(_Extent != dynamic_extent) span(_Rng&& range)
        : _Base(_RANGES data(range), static_cast<size_type>(_RANGES size(range))) {
        #if _CONTAINER_DEBUG_LEVEL > 0
        if constexpr (_Extent != dynamic_extent) {
            _STL_VERIFY(_RANGES size(range) == _Extent, "无法从范围构造具有静态范围的 span");
        }
        #endif
    }
​
    // 从另一个 span 构造
    template <class _OtherTy, size_t _OtherExtent>
    requires (_Extent == dynamic_extent || _OtherExtent == dynamic_extent || _Extent == _OtherExtent)
          && is_convertible_v<_OtherTy (*)[], element_type (*)[]>
    constexpr explicit(_Extent != dynamic_extent && _OtherExtent == dynamic_extent)
        span(const span<_OtherTy, _OtherExtent>& other) noexcept
        : _Base(other.data(), other.size()) {
        #if _CONTAINER_DEBUG_LEVEL > 0
        if constexpr (_Extent != dynamic_extent) {
            _STL_VERIFY(other.size() == _Extent, "无法从另一个 span 构造具有静态范围的 span");
        }
        #endif
    }
​
    // 获取子视图
    template <size_t _Count>
    _NODISCARD constexpr auto first() const noexcept {
        if constexpr (_Extent != dynamic_extent) {
            static_assert(_Count <= _Extent, "范围大小超出限制");
        }
        return span<element_type, _Count>{_Mydata, _Count};
    }
​
    _NODISCARD constexpr auto first(const size_type _Count) const noexcept {
        return span<element_type, dynamic_extent>{_Mydata, _Count};
    }
​
    // 获取子视图(从后面开始)
    template <size_t _Count>
    _NODISCARD constexpr auto last() const noexcept {
        if constexpr (_Extent != dynamic_extent) {
            static_assert(_Count <= _Extent, "范围大小超出限制");
        }
        return span<element_type, _Count>{_Mydata + (_Mysize - _Count), _Count};
    }
​
    _NODISCARD constexpr auto last(const size_type _Count) const noexcept {
        return span<element_type, dynamic_extent>{_Mydata + (_Mysize - _Count), _Count};
    }
​
    // 获取子视图(指定偏移量和大小)
    template <size_t _Offset, size_t _Count = dynamic_extent>
    _NODISCARD constexpr auto subspan() const noexcept {
        using _ReturnType = span<element_type, _Count != dynamic_extent ? _Count : (_Extent != dynamic_extent ? _Extent - _Offset : dynamic_extent)>;
        return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count};
    }
​
    _NODISCARD constexpr auto subspan(const size_type _Offset, const size_type _Count = dynamic_extent) const noexcept {
        return span<element_type, dynamic_extent>{_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count};
    }
​
    // 获取 span 大小(元素个数)
    _NODISCARD constexpr size_t size() const noexcept {
        return _Mysize;
    }
​
    // 获取 span 大小(字节数)
    _NODISCARD constexpr size_type size_bytes() const noexcept {
        return _Mysize * sizeof(element_type);
    }
​
    // 判断是否为空
    _NODISCARD constexpr bool empty() const noexcept {
        return _Mysize == 0;
    }
​
    // 获取指定元素
    _NODISCARD constexpr reference operator[](const size_type off) const noexcept {
        return _Mydata[off];
    }
​
    // 获取第一个元素
    _NODISCARD constexpr reference front() const noexcept {
        return _Mydata[0];
    }
​
    // 获取最后一个元素
    _NODISCARD constexpr reference back() const noexcept {
        return _Mydata[_Mysize - 1];
    }
​
    // 获取指向数据的指针
    _NODISCARD constexpr pointer data() const noexcept {
        return _Mydata;
    }
​
    // 迭代器支持
    _NODISCARD constexpr iterator begin() const noexcept {
        return {_Mydata};
    }
​
    _NODISCARD constexpr iterator end() const noexcept {
        return {_Mydata + _Mysize};
    }
​
    _NODISCARD constexpr reverse_iterator rbegin() const noexcept {
        return reverse_iterator{end()};
    }
​
    _NODISCARD constexpr reverse_iterator rend() const noexcept {
        return reverse_iterator{begin()};
    }
};
举报

相关推荐

0 条评论