主頁 > 後端開發 > C++20初體驗——concepts

C++20初體驗——concepts

2020-12-06 06:10:40 後端開發

引子

凡是涉及STL的錯誤都不堪入目,因為首先STL中有復雜的層次關系,在錯誤資訊中都會暴露出來,其次這么多類和函式的名字大多都是雙下劃線開頭的,一般人看得不習慣,

一個經典的錯誤是給std::sort傳入std::list<T>的迭代器:

#include <list>
#include <algorithm>

int main()
{
    std::list<int> list;
    std::sort(list.begin(), list.end());
}

GCC 10.1.0給出如下錯誤資訊(沒有開-std=c++20):

In file included from c:\program files\mingw-w64\include\c++\10.1.0\algorithm:62,
                 from temp.cpp:3:
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algo.h: In instantiation of 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = std::_List_iterator<int>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]':
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algo.h:4859:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = std::_List_iterator<int>]'
temp.cpp:9:39:   required from here
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algo.h:1975:22: error: no match for 'operator-' (operand types are 'std::_List_iterator<int>' and 'std::_List_iterator<int>')
 1975 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
In file included from c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algobase.h:67,
                 from c:\program files\mingw-w64\include\c++\10.1.0\bits\char_traits.h:39,
                 from c:\program files\mingw-w64\include\c++\10.1.0\ios:40,
                 from c:\program files\mingw-w64\include\c++\10.1.0\ostream:38,
                 from c:\program files\mingw-w64\include\c++\10.1.0\iostream:39,
                 from temp.cpp:1:
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_iterator.h:500:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__y.base() - __x.base())) std::operator-(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_IteratorR>&)'
  500 |     operator-(const reverse_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_iterator.h:500:5: note:   template argument deduction/substitution failed:
In file included from c:\program files\mingw-w64\include\c++\10.1.0\algorithm:62,
                 from temp.cpp:3:
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algo.h:1975:22: note:   'std::_List_iterator<int>' is not derived from 'const std::reverse_iterator<_Iterator>'
 1975 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
In file included from c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algobase.h:67,
                 from c:\program files\mingw-w64\include\c++\10.1.0\bits\char_traits.h:39,
                 from c:\program files\mingw-w64\include\c++\10.1.0\ios:40,
                 from c:\program files\mingw-w64\include\c++\10.1.0\ostream:38,
                 from c:\program files\mingw-w64\include\c++\10.1.0\iostream:39,
                 from temp.cpp:1:
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_iterator.h:1533:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__x.base() - __y.base())) std::operator-(const std::move_iterator<_IteratorL>&, const std::move_iterator<_IteratorR>&)'
 1533 |     operator-(const move_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_iterator.h:1533:5: note:   template argument deduction/substitution failed:
In file included from c:\program files\mingw-w64\include\c++\10.1.0\algorithm:62,
                 from temp.cpp:3:
c:\program files\mingw-w64\include\c++\10.1.0\bits\stl_algo.h:1975:22: note:   'std::_List_iterator<int>' is not derived from 'const std::move_iterator<_IteratorL>'
 1975 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~

太長不看,加三告辭,換個Visual Studio 2019:

Severity	Code	Description	Project	File	Line	Suppression State
Error	C2676	binary '-': 'const std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<_Ty>>>' does not define this operator or a conversion to a type acceptable to the predefined operator	temp	C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\algorithm	4138	
Error	C2672	'_Sort_unchecked': no matching overloaded function found	temp	C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\algorithm	4138	
Error	C2780	'void std::_Sort_unchecked(_RanIt,_RanIt,iterator_traits<_Iter>::difference_type,_Pr)': expects 4 arguments - 3 provided	temp	C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\algorithm	4138	

雖然錯誤資訊簡短許多,但仍不能告訴我們錯誤的原因(這些是內部原因),

我們注意到兩段錯誤都提到了operator-,實際上編譯器認為錯誤在于std::sort中會把兩個輸入迭代器所屬型別的實體相減,而std::list<T>::iterator沒有多載operator-運算子,這當然不是讓我們來多載這個運算子,

STL原始碼可以提供一些幫助:

  /**
   *  @brief Sort the elements of a sequence.
   *  @ingroup sorting_algorithms
   *  @param  __first   An iterator.
   *  @param  __last    Another iterator.
   *  @return  Nothing.
   *
   *  Sorts the elements in the range @p [__first,__last) in ascending order,
   *  such that for each iterator @e i in the range @p [__first,__last-1),  
   *  *(i+1)<*i is false.
   *
   *  The relative ordering of equivalent elements is not preserved, use
   *  @p stable_sort() if this is needed.
  */
  template<typename _RandomAccessIterator>
    _GLIBCXX20_CONSTEXPR
    inline void
    sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
    {
      // concept requirements
      __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
	    _RandomAccessIterator>)
      __glibcxx_function_requires(_LessThanComparableConcept<
	    typename iterator_traits<_RandomAccessIterator>::value_type>)
      __glibcxx_requires_valid_range(__first, __last);
      __glibcxx_requires_irreflexive(__first, __last);

      std::__sort(__first, __last, __gnu_cxx::__ops::__iter_less_iter());
    }

在概念上(conceptually),std::list<T>的迭代器不滿足RandomAccessIterator的要求,所以不能用于std::sort,然而_RandomAccessIterator畢竟只是一個名字,編譯器不知道它表示哪些要求,更無法據此輸出錯誤資訊,

但是從C++20開始,編譯器可以掌握這些資訊了,不是通過typename后面的那個名字,而是由兩個新關鍵詞conceptrequires支撐起來的,然后對于上面那個錯誤,編譯器會說:“std::random_access_iterator<std::list<int>::iterator>不成立”(盡管目前我還沒有體驗過這種編譯器),

如果我們自己寫的模板函式對型別有要求,可以在模板引數串列中寫出:

#include <iterator>

template<std::random_access_iterator Iter>
void func(Iter _first, Iter _last)
{
    // ...
}

那么std::random_access_iterator是如何實作的呢?

template<typename _Iter>
  concept random_access_iterator = bidirectional_iterator<_Iter>
    && derived_from<__detail::__iter_concept<_Iter>,
                    random_access_iterator_tag>
    && totally_ordered<_Iter> && sized_sentinel_for<_Iter, _Iter>
    && requires(_Iter __i, const _Iter __j,
                const iter_difference_t<_Iter> __n)
    {
      { __i += __n } -> same_as<_Iter&>;
      { __j +  __n } -> same_as<_Iter>;
      { __n +  __j } -> same_as<_Iter>;
      { __i -= __n } -> same_as<_Iter&>;
      { __j -  __n } -> same_as<_Iter>;
      {  __j[__n]  } -> same_as<iter_reference_t<_Iter>>;
    };

意思看得懂,但不會寫,別著急,這些語法我們一點點來講,

 

requires關鍵詞與需求

對模板引數的需求是嵌套的,深入到最底層,都是通過requires關鍵詞實作的,“s”的存在使代碼在英語的語法中更加通順一點,

requires有兩種用法:requires子句(requires-clause)和requires運算式,

requires運算式

requires運算式產生一個bool值,語法為下列之一:

  • requires { 一系列requirements(需求) }

  • requires ( 引數串列 ) { 一系列requirements }

引數串列用于創建一系列一定型別的變數,在requirements中使用,這些變數并不真實存在(只有語法功能),它們的作用域到后面的}為止,

Requirements有四種:簡單需求(simple requirements)、型別需求(type requirements)、復合需求(compound requirements)和嵌套需求(nested requirements),Requirements之間由分號分隔,只有當每個都滿足時整個運算式才為true

我們后面再來看requires運算式怎么用,現在我們要了解的是我們可以提出哪些需求,

簡單需求

任意不以requires關鍵詞開頭的運算式都可以作為簡單需求,當該運算式語法正確時需求滿足,由于引數串列中的變數不實際存在,這個運算式當然也不會被求值,

requires (T a, T b)
{
    a + b;
}

型別需求

typename后跟一個型別名成為型別需求,當該型別存在時需求滿足,型別需求可以用來檢查嵌套型別和模板實體化,

requires
{
    typename T::type;
    typename S<T>;
}

復合需求

復合需求要求一個運算式合法,且結果型別符合一定約束,并可規定noexcept

{ 運算式 } 可選的noexcept -> concept名 可選的<引數串列>;

后面會講型別代入concept的規則,畢竟現在連concept都沒講呢,

requires (T x)
{
    {++x} -> std::same_as<T&>;
}

嵌套需求與requires子句

嵌套需求就是requires子句(這句話不太嚴格,但沒有必要糾結它們的區別),requires后跟一個bool常量成為一個requires子句,僅當該bool常量的值為true時,子句所在的需求被滿足,或所在的模板有效,預告一下,把引數代入一個concept可以得到truefalse,而一個concept可以包含多個需求,所以嵌套需求就是多條已定義的需求的組合,

requires (T x) // requires運算式
{
    requires true; // requires子句
    requires std::random_access_iterator<T>; // requires子句,std::random_access_iterator是一個concept
    requires requires (std::size_t n) // 第一個是requires子句,后跟bool值;第二個是requires運算式,產生bool值
    {
        x += n;
    };
}

 

concept

我們一般用concepts(概念)一詞指稱這一套C++20特性,前面介紹了各種需求,它們寫起來比較長,應該用一個名字來概括它,這個名字將成為一個concept

concept的語法很簡單:

template<模板引數串列>
concept 名字 = bool運算式;

bool運算式當然必須是常量運算式,通常是與模板引數串列有關的requires運算式,和其他concept的邏輯組合,concept可以產生bool值,想象一下把concept換成bool當變數模板就可以了,除此以外,concept作為concept可以用在requires子句和requires運算式中,我們稍后再來看其他用法,

concept不能遞回參考自己,concept不能單獨宣告,所以不會出現兩個concept相互參考的情況,下一節將介紹的四種約束,concept一個都不能有,

標準庫定義了許多concept,分布在<concepts><iterator><ranges>中,它們中的一些與<type_traits>is_開頭的型別有相同的含義,但名字不同(而且不是僅僅去掉is_),

分類 名稱 功能
語言核心 same_as 與某型別相同
derived_from 是某型別的子類
convertible_to 可以轉換為某型別
common_reference_with 與某型別有common_type
common_with 與某型別有common_reference
integral 是整型
signed_integral 是帶符號整型
unsigned_integral 是無符號整型
floating_point 是浮點型別
assignable_from 可從某型別賦值
swappable swap
swappable_with 可與某型別swap
destructible 可析構
constructible_from 可由某些型別的引數構造
default_initializable 可默認初始化
move_constructible 可移動構造
copy_constructible 可拷貝構造
比較 equality_comparable ==比較
equality_comparable_with 可與某型別==比較
totally_ordered 可全序比較(==<<=等)
totally_ordered_with 可與某型別全序比較
物件屬性 movable 可移動和swap
copyable 可拷貝且movable
semiregular 可默認構造且copyable
regular equality_comparable && semiregular
可呼叫 invocable 可用某些型別的引數呼叫
regular_invocable invocable且無狀態
predicate bool謂詞
relation 是二元關系
equivalence_relation 是等價(==)關系
strict_weak_order 是嚴格弱序(<)關系

對于最后兩個concept,除了有各種可呼叫的函式的需求以外,==運算子必須滿足自反性與對稱性,<運算子也類似,這些是句法上無法檢查的,所以這兩個concept更像是一種規約:如果模板引數被這種concept約束,那么客戶呼叫時傳入的引數就得滿足這些語意需求,由于concept不能被特化,這一任務只能落到客戶肩上,并且我不認為C++能進化出語意檢查,

有些資料中的標準庫concept是帕斯卡命名(PascalCase)的,因為最初的concept提案中是這樣寫的,原因可能是為了讓它看起來屬于新的C++20,或是與模板引數串列中型別大寫的習慣一致,后來幾個C++元老決定把concept換回C++標準命名法(Rename concepts to standard_case for C++20, while we still can),單詞組成也略有修改,后來又有少許修改,以最新標準草稿(寫作時為N4868)為準,

約束

現在到了應用concept的時候了,Constraint(約束)指定模板引數的需求,是以下需求的邏輯與:

  1. 模板引數前的concept;

    template<Concept T> // `Concept`是一個concept,下同
    void f(T);
    
  2. 模板引數串列后的requires子句;

    template<typename T>
        requires Concept<T>
    void f(T);
    
  3. 在簡略函式模板宣告(用auto替代模板型別,C++20特性)中,型別占位符(auto)前的concept;

    void f(Concept auto _arg);
    

    說來慚愧,寫C++這么久,我從來沒有過簡寫模板型別為auto的想法,明明是知道泛型lambda的,

  4. 在函式宣告最后的requires子句,

    template<typename T>
    void f(T) requires Concept<T>;
    

這些requirements當然可以同時存在:

template<Concept1 T>
    requires Concept2<T>
void f(T) requires Concept3<T>;

Concept2<T>Concept3<T>都在requires子句中,產生truefalse,任意一個為false時該實體化無效,

但是如何理解Concept1 T呢?把T插到Concept1的引數串列的最前面,這里為空,所以就是Concept1<T>,另一個應用這一規則的地方是復合需求的回傳型別部分,我們寫std::same_as<int>,其含義為requires std::same_as<T, int>(但是不能這么寫),

如果模板引數代入時出現了不存在的型別或變數,該約束僅僅是不被滿足,而不會產生編譯錯誤,

約束可以用于函式模板、類模板和成員函式,非模板類的非模板成員函式除外,函式模板與類模板的約束是類似的,只有滿足約束時模板才能實體化;對于成員函式的約束,如果它作用于模板類的模板引數,當約束不滿足時,并不是類模板不能被實體化,而是實體化后的模板類沒有這個成員函式:

#include <concepts>

template<std::regular T>
struct Container
{
    template<std::same_as<int> U>
    void f(U u) { }
    
    void g()
        requires std::same_as<T, int>
    { }
};

int main()
{
    Container<int> ci;
    ci.f(1);
    ci.g();
    Container<double> cd;
    cd.f(1);
    cd.g(); // error
}

像特化和偏特化一樣,concept之間存在的包含關系也能用于多載決議——如果A成立則B一定成立,那么實體化時會優先匹配B的那一個實作,但是,concept的包含關系有時會不符合直覺,即兩個concept看似包含卻不能被編譯器發現:

template<class T> constexpr bool is_meowable = true;
template<class T> constexpr bool is_cat = true;
 
template<class T>
concept Meowable = is_meowable<T>;
 
template<class T>
concept BadMeowableCat = is_meowable<T> && is_cat<T>;
 
template<class T>
concept GoodMeowableCat = Meowable<T> && is_cat<T>;
 
template<Meowable T>
void f1(T); // #1
 
template<BadMeowableCat T>
void f1(T); // #2
 
template<Meowable T>
void f2(T); // #3
 
template<GoodMeowableCat T>
void f2(T); // #4
 
void g(){
    f1(0); // error, ambiguous:
           // the is_meowable<T> in Meowable and BadMeowableCat forms distinct
           // atomic constraints that are not identical (and so do not subsume each other)
 
    f2(0); // OK, calls #4, more constrained than #3
           // GoodMeowableCat got its is_meowable<T> from Meowable
}

如果Meowable<T>,那么一定有is_meowable<T>,所以BadMeowableCat<T>也滿足,為什么不能判斷出MeowableBadMeowableCat之間的包含關系呢?包含關系作用在由&&||連接的邏輯運算式上(實際上是合取與析取),通過深入到判斷兩個原子的(不是&&||連接的)運算式是否相同從而決定包含關系,而只有相同的concept加上相同的模板引數才是相同,其他運算式即使再長得一樣也是不同的,

在上面的例子中,編譯器認為BadMeowableCat中的is_meowableMeowable中的那個不一樣,從而兩個concept之間沒有包含關系,于是f1的多載決議就是二義的;而GoodMeowableCat顯然包含了Meowable,所以對f2的呼叫就是合法的,

另一方面,包含關系的檢查一定會深入到最底層的concept,所以沒有必要給所有自定義的concept進行非常嚴格的層次劃分,但是有一點是原則性的,就是當你需要不同約束程度的concept時,它們的最底層必須都被有名字的concept封裝起來,<type_traits>里有那么多變數模板,<concepts>還要分別用不同的、有些混淆性的名字包裝一下,正是因為這個,

模板升級

面向程序、基于物件、面向物件、泛型和函式式這幾個編程范式是逐漸加入C++的,起初,C++并沒有模板,直到1990年,Bjarne Stroustrup對模板的要求是(以下翻譯了跟沒翻一樣):

  • Full generality/expressiveness

  • Zero overhead compared to hand coding

  • Well-specified interfaces

后來的實作滿足了前兩條:針對第一條,C++模板是圖靈完全的;針對第二條,C++模板帶來更好的運行時性能(相比于qsort或虛函式這一類實作);唯獨第三條沒有解決,導致冗長的模板錯誤,并且衍生出以SFINAE為代表的一些奇技淫巧,它們貫穿我之前寫的<functional>系列,成功勸退了很多讀者,

C++20帶來了解決方案——concept與約束,實際上concept早在零幾年就出現在C++標準的草稿里了,但在2009年被洗掉,沒有進入C++11(這一套工具非常復雜,C++20中只是它的簡化版),后來組委會又嘗試了concepts lite,但也沒有進入C++17,與此同時有一條支線concepts TS在發展,并在GCC中實作了出來,以此積累經驗,C++20中的concept與TS還有一定區別,是總結了concept的各種實作以后選擇的,

現在我們就來看一下concept如何給模板編程進行升級,以下例子來自meds::function,是我為一個華麗而無用的單片機專案寫的庫,

Tag Dispatching

首先是還講點道理的tag dispatching,S是用來放物件的空間的型別,T是要放的物件的型別,一個T能否放進一個S將決定initialize等一系列操作的方法,而object_manager對外提供一個介面,在內部進行分類討論:

template<typename S, typename T>
class object_manager
{
private:
    using local_storage = std::integral_constant<bool,
            std::is_trivially_copy_constructible<T>::value
        && sizeof(T) <= sizeof(S)
        && alignof(S) % alignof(T) == 0
    >;

public:
    static void initialize(S* _tar, T&& _obj)
    {
        initialize(_tar, std::move(_obj), local_storage());
    }
    
private:
    static void initialize(S* _tar, T&& _obj, std::true_type )
    {
        new (reinterpret_cast<T*>(_tar)) T(std::move(_obj));
    }
    
    static void initialize(S* _tar, T&& _obj, std::false_type)
    {
        _tar->template reinterpret_as<T*>() = new T(std::move(_obj));
    }
};

T可以放進S時,local_storage將成為true_type,匹配到第二個initialize,反之則為第三個,

這種操作還可以接受,但有了concept以后會更好:

template<typename S, typename T>
concept locally_storable = std::is_trivially_copy_constructible<T>::value
                        && sizeof(T) <= sizeof(S)
                        && alignof(S) % alignof(T) == 0;

template<typename S, typename T>
class object_manager
{
public:
    static void initialize(S* _tar, T&& _obj)
    {
        reinterpret_cast<T*&>(*_tar) = new T(std::move(_obj));
    }
    
    static void initialize(S* _tar, T&& _obj) requires locally_storable<S, T>
    {
        new (reinterpret_cast<T*>(_tar)) T(std::move(_obj));
    }
};

SFINAE

然后就是不講章法的SFINAE了,下面我們要根據一個類的可比較性呼叫不同實作,分為兩步:function_eq_comp中定義了value指示模板引數T型別的兩個實體是否可以用operator==比較,function_object_compare根據其結果執行不同操作,

template<typename T>
class function_eq_comp
{
private:
    using one = int;
    struct two
    {
        one unused[2];
    };

    template <typename U,
        typename = decltype(std::declval<U>() == std::declval<U>())>
    static one test(int);
    template <typename>
    static two test(...);

public:
    static constexpr bool value = https://www.cnblogs.com/jerry-fuyi/archive/2020/12/05/sizeof(decltype(test(0))) == sizeof(one);
};

template
typename std::enable_if< function_eq_comp::value, bool>::type
    function_object_compare(const T& _lhs, const T& _rhs)
{
    return _lhs == _rhs;
}

template
typename std::enable_if::value, bool>::type
    function_object_compare(const T& _lhs, const T& _rhs)
{
    return false;
}

==運算子可用時,one test(int)函式正確定義,test函式的回傳型別將會是onevaluetrue,否則one test(int)錯誤,根據SFINAE,test的呼叫落入two test(...)valuefalse

當兩個const T&不可比較時,function_eq_comp<const T&>::valuefalsestd::enable_if沒有定義type,第一個function_object_compare的模板型別發生錯誤,根據SFINAE,該多載被忽略;與此同時第二個是可用的,反之,會呼叫到第一個,與tag dispatching中true_typefalse_type并列出現類似,function_eq_comp<const T&>::value與它取!的運算式也都得出現,不能像上面的concept實作那樣利用兩個函式之間由多載優先級建立起的層次關系,與上一節相比,這里的代碼重復更惡心一點,

concept寫會好看很多,尤其是在檢查operator==可以用std::equality_comparable的前提下:

template<typename T>
bool function_object_compare(const T& _lhs, const T& _rhs)
{
    return false;
}

template<typename T>
bool function_object_compare(const T& _lhs, const T& _rhs)
    requires std::equality_comparable<const T&>
{
    return _lhs == _rhs;
}

思考題

  1. 下面這段代碼錯在哪?

    template<typename T, typename U>
        requires (T t, U u) { t + u; }
    auto add(T t, U u)
    {
        return t + u;
    }
    
  2. * 查閱資料,寫出一個嵌套需求接受但templaterequires子句不接受的運算式,(這道題沒什么意義,只是想讓你去查點資料,)

  3. 不查閱資料,判斷std::derived_from的兩個引數(基類、子類)哪個在前,并給出判斷依據,

  4. 如何給一個函式添加約束,使得它能接受任意數量的相同型別的引數?

  5. 試用concept改寫一個void_t技巧的實體,

擴展閱讀

Constraints and concepts

C++20: Two Extremes and the Rescue with Concepts等一系列文章

Does constraint subsumption only apply to concepts?

The tightly-constrained design space of convenient syntaxes for generic programming

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230519.html

標籤:其他

上一篇:SpringBoot進階教程(六十六)Docker部署SpringBoot專案

下一篇:存在可能替代C語言的其他語言嗎?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more