精华内容
下载资源
问答
  • C++ const 和 constexpr 的区别呢,constexpr表示这玩意儿在编译期就可以算出来(前提是为了算出它所依赖的东西也是在编译期可以算出来的)。而const只保证了运行时不直接被修改(但这个东西仍然可能是个动态变量)...
  • constexpr-to-string 特征: 在编译时将任何整数类型转换为字符串 支持转换为2到36之间(包括2和36)的任意基数 没有外部依赖关系,仅包括用于模板参数检查的type_traits 支持自定义字符类型,例如to_string C +...
  • constexpr Murmur3A哈希 这是constexpr(C ++ 14)实现或MurMur3A,可以使用clang或gcc进行编译。 有关更多详细信息,请参阅我关于此库的。 用法 这是仅标头的库。 包括头文件constexpr_murmur3.h并调用函数ce_mm3::...
  • Constexpr SQL 轻巧的单头替代DBMS 该库是在我在( 的监督下,在获得荣誉项目期间开发的。 最初的开发发生在此存储库中,但后来转移到了一个新的专用家庭存储库中。 该项目受到出色的库(CTRE)的启发和影响。 ...
  • constexpr

    2021-01-30 22:12:51
    constexpr修饰值的时候,只能修饰常量,其值在编译器就确定。 constexpr修饰函数的时候,函数要有返回值,但不要求返回值为编译器常量。编译器会检测调用该函数时在编译器能否得到值,如果可以编译器会对它进行优化...

    constexpr修饰值的时候,只能修饰常量,其值在编译期就确定。

    constexpr修饰函数的时候,函数要有返回值,但不要求返回值为编译器常量。编译器会检测调用该函数时在编译器能否得到值,如果可以编译器会对它进行优化否则就当做普通函数。所以可以用constexpr的函数就尽量使用。

    函数只有足够简单才能在编译器求值。constexpr函数必须不能有循环、没有局部变量。

    当函数加上了constexpr但没有遵循相应的规则,那么编译时间会边长,有了错误调试难度也会增加。

    当constexpr修饰构造函数的时候,规则和上面有所区别,只有一条:只允许执行成员初始化操作(构造函数不是真正的函数,无法或者构造函数的地址的)。

    int main(int argc, char *argv[])
    {
        int j = 9;
        constexpr int i = 99 + j;//错误
        const int i = 99 + j;//正确
    }

     这段代码可以编译通过:

    constexpr int getValue(int value)
    {
        return value * 2.0;
    }
    
    #define debug qDebug()<<
    int main(int argc, char *argv[])
    {
        debug getValue(666);
    }

    参考:C++ const 和 constexpr 的区别?

    展开全文
  • C ++ 20的Unconstexpr ...constexpr auto counter = unconstexpr::meta_value{}; static_assert (counter++ == 0 ); static_assert (counter++ == 1 ); static_assert (*counter == 2 ); 更令人兴奋的: // li
  • 编译时xxhash实现 作为C ++ 11 constexpr表达式的64位xxhash算法的实现(请参阅 )。
  • Sprout C ++库欢迎使用Sprout C ++库C ++ 11/14基于constexpr的容器,算法,随机数,解析,光线跟踪,合成器等。 图书馆文档Sprout C ++库欢迎使用Sprout C ++库C ++ 11/14基于constexpr的容器,算法,随机数,解析...
  • C++中的const和constexpr

    2020-12-22 23:42:35
    C++中的const可用于修饰变量、函数,且在不同的地方有着不同的含义,现总结如下。  Const的语义  C++中的const的目的是通过编译器来保证对象的常量性,强制编译器将所有可能违背const对象的常量性的操作都视为...
  • 关于in_constexpr 一种检测constexpr函数中是否在constexpr上下文中的方法。 通过能够检测是否在constexpr上下文中,我们可以选择实现特定于运行时的算法,同时在编译时可以使用不同的算法来执行某些操作。 该...
  • What's the difference between constexpr and const ? constexpr和const什么区别? When can I use only on

    本文翻译自:Difference between `constexpr` and `const`

    What's the difference between constexpr and const ? constexprconst什么区别?

    • When can I use only one of them? 什么时候只能使用其中之一?
    • When can I use both and how should I choose one? 什么时候可以同时使用两者?应该如何选择?

    #1楼

    参考:https://stackoom.com/question/xEDT/constexpr和const之间的区别


    #2楼

    const applies for variables , and prevents them from being modified in your code. const适用于变量 ,并防止它们在您的代码中被修改

    constexpr tells the compiler that this expression results in a compile time constant value , so it can be used in places like array lengths, assigning to const variables, etc. The link given by Oli has a lot of excellent examples. constexpr告诉编译器该表达式产生一个编译时间常数值 ,因此可以在诸如数组长度,分配给const变量等地方使用。Oli提供的链接有很多很好的例子。

    Basically they are 2 different concepts altogether, and can (and should) be used together. 基本上,它们总共是2个不同的概念,可以(并且应该)一起使用。


    #3楼

    Basic meaning and syntax 基本含义和语法

    Both keywords can be used in the declaration of objects as well as functions. 这两个关键字都可以在对象和函数的声明中使用。 The basic difference when applied to objects is this: 应用于对象的基本区别是:

    • const declares an object as constant . const声明一个对象为常量 This implies a guarantee that, once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. 这意味着保证,一旦初始化,该对象的值就不会改变,并且编译器可以利用这一事实进行优化。 It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization. 它还有助于防止程序员编写代码来修改在初始化后不希望修改的对象。

    • constexpr declares an object as fit for use in what the Standard calls constant expressions . constexpr声明一个对象适合在标准调用的常量表达式中使用 But note that constexpr is not the only way to do this. 但是请注意, constexpr并非唯一的方法。

    When applied to functions the basic difference is this: 当应用于函数时 ,基本区别是:

    • const can only be used for non-static member functions, not functions in general. const只能用于非静态成员函数,而不能用于一般函数。 It gives a guarantee that the member function does not modify any of the non-static data members. 它保证成员函数不会修改任何非静态数据成员。

    • constexpr can be used with both member and non-member functions, as well as constructors. constexpr可以与成员函数和非成员函数以及构造函数一起使用。 It declares the function fit for use in constant expressions . 它声明该函数适合在常量表达式中使用。 The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly (†) : 仅当函数满足某些条件(7.1.5 / 3,4),最重要的是(†)时 ,编译器才会接受它:

      • The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single return statement is allowed. 函数体必须是非虚拟的,并且必须非常简单:除了typedef和static断言,仅允许单个return语句。 In the case of a constructor, only an initialization list, typedefs and static assert are allowed. 对于构造函数,仅允许使用初始化列表,typedef和静态断言。 ( = default and = delete are allowed, too, though.) (不过,也允许使用= default= delete 。)
      • As of C++14 the rules are more relaxed, what is allowed since then inside a constexpr function: asm declaration, a goto statement, a statement with a label other than case and default , try-block, definition of a variable of non-literal type, definition of a variable of static or thread storage duration, definition of a variable for which no initialization is performed. 从C ++ 14开始,规则更加宽松,此后允许在constexpr函数中使用: asm声明, goto语句,带有除casedefault之外的标签的语句,try-block,non变量的定义类型类型,静态或线程存储持续时间变量的定义,未执行初始化的变量的定义。
      • The arguments and the return type must be literal types (ie, generally speaking, very simple types, typically scalars or aggregates) 参数和返回类型必须是文字类型 (即,通常来说,非常简单的类型,通常是标量或集合)

    Constant expressions 常数表达式

    As said above, constexpr declares both objects as well as functions as fit for use in constant expressions. 如上所述, constexpr声明两个对象以及适合在常量表达式中使用的函数。 A constant expression is more than merely constant: 常量表达式不仅仅是常量:

    • It can be used in places that require compile-time evaluation, for example, template parameters and array-size specifiers: 它可以在需要编译时评估的地方使用,例如模板参数和数组大小说明符:

       template<int N> class fixed_size_list { /*...*/ }; fixed_size_list<X> mylist; // X must be an integer constant expression int numbers[X]; // X must be an integer constant expression 
    • But note: 但请注意:

      • Declaring something as constexpr does not necessarily guarantee that it will be evaluated at compile time. 将某些东西声明为constexpr并不一定保证它将在编译时进行评估。 It can be used for such, but it can be used in other places that are evaluated at run-time, as well. 可以用于此目的 ,但也可以在运行时评估的其他地方使用。

      • An object may be fit for use in constant expressions without being declared constexpr . 一个对象可能适合在常量表达式中使用, 而无需声明constexpr Example: 例:

         int main() { const int N = 3; int numbers[N] = {1, 2, 3}; // N is constant expression } 

      This is possible because N , being constant and initialized at declaration time with a literal, satisfies the criteria for a constant expression, even if it isn't declared constexpr . 这是可能的,因为N是常量,并且在声明时使用文字立即初始化,即使未声明constexpr ,它也满足常量表达式的条件。

    So when do I actually have to use constexpr ? 那么,我什么时候真正必须使用constexpr

    • An object like N above can be used as constant expression without being declared constexpr . 像上面的N这样的对象可以用作常量表达式, 而无需声明constexpr This is true for all objects that are: 对于以下所有对象都是如此:

      • const
      • of integral or enumeration type and 整型或枚举类型的,并且
      • initialized at declaration time with an expression that is itself a constant expression 在声明时使用本身就是常量表达式的表达式进行初始化

      [This is due to §5.19/2: A constant expression must not include a subexpressions that involves "an lvalue-to-rvalue modification unless […] a glvalue of integral or enumeration type […]" Thanks to Richard Smith for correcting my earlier claim that this was true for all literal types.] [这是由于第5.19 / 2条所致:常量表达式不得包含涉及“从左值到右值的修改,除非[...]整数或枚举类型的[...]的glvalue”的子表达式”感谢Richard Smith纠正了早些时候声称对所有文字类型都是如此。]

    • For a function to be fit for use in constant expressions, it must be explicitly declared constexpr ; 为了使函数适合在常量表达式中使用, 必须将其显式声明为constexpr it is not sufficient for it merely to satisfy the criteria for constant-expression functions. 仅满足常数表达式函数的标准是不够的。 Example: 例:

       template<int N> class list { }; constexpr int sqr1(int arg) { return arg * arg; } int sqr2(int arg) { return arg * arg; } int main() { const int X = 2; list<sqr1(X)> mylist1; // OK: sqr1 is constexpr list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr } 

    When can I / should I use both, const and constexpr together? 我什么时候可以同时使用constconstexpr

    A. In object declarations. A.在对象声明中。 This is never necessary when both keywords refer to the same object to be declared. 当两个关键字都引用相同的要声明的对象时,就永远不需要这样做。 constexpr implies const . constexpr暗示const

    constexpr const int N = 5;
    

    is the same as 是相同的

    constexpr int N = 5;
    

    However, note that there may be situations when the keywords each refer to different parts of the declaration: 但是,请注意,在某些情况下,关键字每个都引用声明的不同部分:

    static constexpr int N = 3;
    
    int main()
    {
      constexpr const int *NP = &N;
    }
    

    Here, NP is declared as an address constant-expression, ie an pointer that is itself a constant expression. 此处, NP被声明为地址常量表达式,即本身就是常量表达式的指针。 (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr and const are required: constexpr always refers to the expression being declared (here NP ), while const refers to int (it declares a pointer-to-const). (当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)在这里, constexprconst都是必需的: constexpr始终引用要声明的表达式(此处为NP ),而const引用int (它声明了一个指向常量的指针)。 Removing the const would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N is in-fact a pointer-to-constant). 删除const将使表达式非法(因为(a)指向非const对象的指针不能是常量表达式,并且(b) &N实际上是指向常量的指针)。

    B. In member function declarations. B.在成员函数声明中。 In C++11, constexpr implies const , while in C++14 and C++17 that is not the case. 在C ++ 11中, constexpr隐含const ,而在C ++ 14和C ++ 17中则并非如此。 A member function declared under C++11 as 在C ++ 11下声明为的成员函数为

    constexpr void f();
    

    needs to be declared as 需要声明为

    constexpr void f() const;
    

    under C++14 in order to still be usable as a const function. 为了仍然可用作const函数,请在C ++ 14下使用。


    #4楼

    According to book of "The C++ Programming Language 4th Editon" by Bjarne Stroustrup 根据Bjarne Stroustrup撰写的“ The C ++ Programming Language 4th Editon”一书
    const : meaning roughly ''I promise not to change this value'' (§7.5). const :大致意思是“我保证不会更改此值”(第7.5节)。 This is used primarily to specify interfaces, so that data can be passed to functions without fear of it being modified. 这主要用于指定接口,以便可以将数据传递给函数而不必担心会被修改。
    The compiler enforces the promise made by const. 编译器执行const作出的承诺。
    constexpr : meaning roughly ''to be evaluated at compile time'' (§10.4). constexpr :大致意思是“在编译时求值”(第10.4节)。 This is used primarily to specify constants, to allow 这主要用于指定常量,以允许
    For example: 例如:

    const int dmv = 17; // dmv is a named constant
    int var = 17; // var is not a constant
    constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
    constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
    const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
    double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
    vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
    const double s1 = sum(v); // OK: evaluated at run time
    constexpr double s2 = sum(v); // error : sum(v) not constant expression
    

    For a function to be usable in a constant expression, that is, in an expression that will be evaluated by the compiler, it must be defined constexpr . 为了使函数在常量表达式(即将由编译器求值的表达式)中使用,必须将其定义为constexpr
    For example: 例如:

    constexpr double square(double x) { return x∗x; }
    


    To be constexpr, a function must be rather simple: just a return-statement computing a value. 要成为constexpr,函数必须非常简单:仅是一个计算值的返回语句。 A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression. constexpr函数可用于非恒定参数,但这样做的结果不是恒定表达式。 We allow a constexpr function to be called with non-constant-expression arguments in contexts that do not require constant expressions, so that we don't hav e to define essentially the same function twice: once for constant expressions and once for variables. 我们允许在不需要常量表达式的上下文中使用非常量表达式参数调用constexpr函数,因此我们不必两次定义本质上相同的函数:一次用于常量表达式,一次用于变量。
    In a few places, constant expressions are required by language rules (eg, array bounds (§2.2.5, §7.3), case labels (§2.2.4, §9.4.2), some template arguments (§25.2), and constants declared using constexpr). 在某些地方,语言规则(例如,数组边界(第2.2.5节,第7.3节),大小写标签(第2.2.4节,第9.4.2节),一些模板参数(第25.2节)和使用constexpr声明的常量)。 In other cases, compile-time evaluation is important for performance. 在其他情况下,编译时评估对于性能很重要。 Independently of performance issues, the notion of immutability (of an object with an unchangeable state) is an important design concern (§10.4). 与性能问题无关,(具有不变状态的对象的)不变性的概念是一个重要的设计问题(第10.4节)。


    #5楼

    Overview 总览

    • const guarantees that a program does not change an object's value . const保证程序不会更改对象的value However, const does not guarantee which type of initialization the object undergoes. 但是, const不保证对象将进行哪种类型的初始化。

      Consider: 考虑:

       const int mx = numeric_limits<int>::max(); // OK: runtime initialization 

      The function max() merely returns a literal value. 函数max()仅返回文字值。 However, because the initializer is a function call, mx undergoes runtime initialization. 但是,由于初始化程序是函数调用,因此mx会进行运行时初始化。 Therefore, you cannot use it as a constant expression : 因此,不能将其用作常量表达式

       int arr[mx]; // error: “constant expression required” 
    • constexpr is a new C++11 keyword that rids you of the need to create macros and hardcoded literals. constexpr是一个新的C ++ 11关键字,使您无需创建宏和硬编码的文字。 It also guarantees, under certain conditions, that objects undergo static initialization . 它还可以确保在某些条件下对象可以进行静态初始化 It controls the evaluation time of an expression. 它控制表达式的评估时间。 By enforcing compile-time evaluation of its expression , constexpr lets you define true constant expressions that are crucial for time-critical applications, system programming, templates, and generally speaking, in any code that relies on compile-time constants. 通过对表达式的编译时求值constexpr可以让您定义真正的常量表达式 ,这些表达式对于依赖于时间的应用程序,系统编程,模板以及通常来说对于依赖编译时常量的任何代码都至关重要。

    Constant-expression functions 常数表达函数

    A constant-expression function is a function declared constexpr . 常量表达式函数是声明为constexpr的函数。 Its body must be non-virtual and consist of a single return statement only, apart from typedefs and static asserts. 它的主体必须是非虚拟的,并且除了typedef和static断言外,只能由单个return语句组成。 Its arguments and return value must have literal types. 它的参数和返回值必须具有文字类型。 It can be used with non-constant-expression arguments, but when that is done the result is not a constant expression. 可以将其与非常量表达式参数一起使用,但是完成后的结果将不是常量表达式。

    A constant-expression function is meant to replace macros and hardcoded literals without sacrificing performance or type safety. 常量表达式函数旨在替换硬编码文字,而不会牺牲性能或类型安全性。

    constexpr int max() { return INT_MAX; }           // OK
    constexpr long long_max() { return 2147483647; }  // OK
    constexpr bool get_val()
    {
        bool res = false;
        return res;
    }  // error: body is not just a return statement
    
    constexpr int square(int x)
    { return x * x; }  // OK: compile-time evaluation only if x is a constant expression
    const int res = square(5);  // OK: compile-time evaluation of square(5)
    int y = getval();
    int n = square(y);          // OK: runtime evaluation of square(y)
    

    Constant-expression objects 常量表达式对象

    A constant-expression object is an object declared constexpr . 常量表达式对象是声明为constexpr的对象。 It must be initialized with a constant expression or an rvalue constructed by a constant-expression constructor with constant-expression arguments. 它必须使用常量表达式或由带有常量表达式参数的常量表达式构造函数构造的右值初始化。

    A constant-expression object behaves as if it was declared const , except that it requires initialization before use and its initializer must be a constant expression. 常量表达式对象的行为就像声明为const ,只是它在使用前需要进行初始化并且其初始化程序必须是常量表达式。 Consequently, a constant-expression object can always be used as part of another constant expression. 因此,常量表达式对象始终可以用作另一个常量表达式的一部分。

    struct S
    {
        constexpr int two();      // constant-expression function
    private:
        static constexpr int sz;  // constant-expression object
    };
    constexpr int S::sz = 256;
    enum DataPacket
    {
        Small = S::two(),  // error: S::two() called before it was defined
        Big = 1024
    };
    constexpr int S::two() { return sz*2; }
    constexpr S s;
    int arr[s.two()];  // OK: s.two() called after its definition
    

    Constant-expression constructors 常量表达式构造函数

    A constant-expression constructor is a constructor declared constexpr . 常量表达式构造函数是声明为constexpr的构造函数。 It can have a member initialization list but its body must be empty, apart from typedefs and static asserts. 它可以有一个成员初始化列表,但是除了typedef和static断言之外,它的主体必须为空。 Its arguments must have literal types. 它的参数必须具有文字类型。

    A constant-expression constructor allows the compiler to initialize the object at compile-time, provided that the constructor's arguments are all constant expressions. 常量表达式构造函数允许编译器在编译时初始化对象,前提是构造函数的参数均为常量表达式。

    struct complex
    {
        // constant-expression constructor
        constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
        // constant-expression functions
        constexpr double real() { return re; }
        constexpr double imag() { return im; }
    private:
        double re;
        double im;
    };
    constexpr complex COMP(0.0, 1.0);         // creates a literal complex
    double x = 1.0;
    constexpr complex cx1(x, 0);              // error: x is not a constant expression
    const complex cx2(x, 1);                  // OK: runtime initialization
    constexpr double xx = COMP.real();        // OK: compile-time initialization
    constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
    complex cx3(2, 4.6);                      // OK: runtime initialization
    

    Tips from the book Effective Modern C++ by Scott Meyers about constexpr : 斯科特·迈耶斯(Scott Meyers)写的《 有效的现代C ++》中有关constexpr

    • constexpr objects are const and are initialized with values known during compilation; constexpr对象是const,并使用编译期间已知的值进行初始化;
    • constexpr functions produce compile-time results when called with arguments whose values are known during compilation; constexpr函数在编译期间用其值已知的参数调用时会产生编译时结果。
    • constexpr objects and functions may be used in a wider range of contexts than non- constexpr objects and functions; 与非constexpr对象和函数相比, constexpr对象和函数可以在更广泛的上下文中使用。
    • constexpr is part of an object's or function's interface. constexpr是对象或函数接口的一部分。

    Source: Using constexpr to Improve Security, Performance and Encapsulation in C++ . 资料来源: 使用constexpr改善C ++的安全性,性能和封装


    #6楼

    As @0x499602d2 already pointed out, const only ensures that a value cannot be changed after initialization where as constexpr (introduced in C++11) guarantees the variable is a compile time constant. 正如@ 0x499602d2已经指出的那样, const仅确保初始化后不能更改值,而constexpr (在C ++ 11中引入)保证变量是编译时间常数。
    Consider the following example(from LearnCpp.com): 考虑以下示例(来自LearnCpp.com):

    cout << "Enter your age: ";
    int age;
    cin >> age;
    
    const int myAge{age};        // works
    constexpr int someAge{age};  // error: age can only be resolved at runtime
    
    展开全文
  • constexpr关键字

    2021-01-08 05:00:16
    constexpr是C++11引入的关键字,为什么在有const关键字的基础上要引入constexpr呢?它们二者有什么区别呢? 简介 C++编译时可确定常量表达式的结果,因此可在编译时优化。 C++规范在一些地方要求使用常量表达式,如...
  • RAII noexcept constexpr

    2020-01-08 12:40:30
    static constexpr T lowest() noexcept { return T(); } ... 又比如,在C++98中,new可能会包含一些抛出的std::bad_alloc异常。 void* operator new(std::size_t) throw(std::bad_alloc); void* operator ...

    //可回退异常处理
    #include <iostream>

    using namespace std;

    class Fruit{
        public:
            Fruit(double n):number(n){}
            virtual ~Fruit(){}

            double number;
    };

    int main()
    {
        Fruit fru1(3);
        Fruit fru2 = fru1;
        try{
            
            fru2.number = 5;
            throw(2);
        }catch(...)
        {
            cout << "bug" << endl;
        }
        //如果没有bug
        fru1 = fru2;

        return 0;
    }
    C++ RAII

        RAII是resource acquisition is initialization的缩写,意为“资源获取即初始化”。它是C++之父Bjarne Stroustrup提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在RAII的指导下,C++把底层的资源管理问题提升到了对象生命周期管理的更高层次。
        说起来,RAII的含义倒也不算复杂。用白话说就是:在类的构造函数中分配资源,在析构函数中释放资源。这样,当一个对象创建的时候,构造函数会自动地被调用;而当这个对象被释放的时候,析构函数也会被自动调用。于是乎,一个对象的生命期结束后将会不再占用资源,资源的使用是安全可靠的。
    C++ RAII体现出了简洁、安全、实时的特点:

    1.概念简洁性:让资源(包括内存和非内存资源)和对象的生命周期绑定,资源类的设计者只需用在类定义内部处理资源问题,提高了程序的可维护性
    2.类型安全性:通过资源代理对象包装资源(指针变量),并利用运算符重载提供指针运算方便使用,但对外暴露类型安全的接口
    3.异常安全性:栈语义保证对象析构函数的调用,提高了程序的健壮性
    4.释放实时性:和GC相比,RAII达到了和手动释放资源一样的实时性,因此可以承担底层开发的重任

    也许你还在惊讶RAII如此简单的时候,关于RAII的主要内容已经介绍完了。简单不意味着简陋,在我看来RAII虽然不像GC一样,是一套具体的机制,但它蕴含的对象与资源关系的哲学深度的理解却使得我对Bjarne Stroustrup肃然起敬!

    最后,不得不提醒RAII的理念固然简单,不过在具体实现的时候仍有需要小心的地方。比如对于STL的auto_ptr,可以视为资源的代理对象,auto_ptr对象间的赋值是一个需要特别注意的地方。简单说来资源代理对象间赋值的语义不满足“赋值相等”,其语义是资源管理权的转移。

    什么是“赋值相等”呢?比如:

    int a;  int b = 10;  a = b; //这句话执行后 a == b 但对于资源代理对象,这是不满足的,比如:

    auto_ptr<int> a(null);  auto_ptr<int> b(new int(123));  a = b; //这句话执行后a != b,赋值的语义是b把资源的管理权交给了a 

    auto_ptr是这样一种指针:它是“它所指向的对象”的拥有者。这种拥有具有唯一性,即一个对象只能有一个拥有者,严禁一物二主。当auto_ptr指针被摧毁时,它所指向的对象也将被隐式销毁,即使程序中有异常发生,auto_ptr所指向的对象也将被销毁。


    关于auto_ptr的几种注意事项:
    1、auto_ptr不能共享所有权。
    2、auto_ptr不能指向数组
    3、auto_ptr不能作为容器的成员。
    4、不能通过赋值操作来初始化auto_ptr
    std::auto_ptr<int> p(new int(42));     //OK
    std::auto_ptr<int> p = new int(42);    //ERROR
    这是因为auto_ptr 的构造函数被定义为了explicit
    5、不要把auto_ptr放入容器

    下面便是在C++中实现RAII的典型代码:
    class file
    {
    public:
        file(string const& name) {
            m_fileHandle=fopen(name.cstr());
        }
        ~file() {
            fclose(m_fileHandle);
        }
        //
    private:
        handle m_fileHandle;
    }

    很典型的“在构造函数里获取,在析构函数里释放”。如果我写下代码:   
    void fun1() {
        file myfile("my.txt");
         //操作文件
    } //此处销毁对象,调用析构函数,释放资源
    当函数结束时,局部对象myfile的生命周期也结束了,析构函数便会被调用,资源会得到释放。而且,如果函数中的代码抛出异常,那么析构函数也会被调用,资源同样会得到释放。所以,在RAII下,不仅仅资源安全,也是异常安全的。

    但是,在如下的代码中,资源不是安全的,尽管我们实现了RAII:
    void fun2() {
        file pfile=new file("my.txt");
         //操作文件
    }
    因为我们在堆上创建了一个对象(通过new),但是却没有释放它。我们必须运用delete操作符显式地加以释放:
    void fun3() {
        file pfile=new file("my.txt");
         //操作文件
            delete pfile;
    }
    否则,非但对象中的资源得不到释放,连对象本身的内存也得不到回收。(将来,C++的标准中将会引入GC(垃圾收集),但正如下面分析的那样,GC依然无法确保资源的安全)。
    现在,在fun3(),资源是安全的,但却不是异常安全的。因为一旦函数中抛出异常,那么delete pfile;这句代码将没有机会被执行。

    C++领域的诸位大牛们告诫我们:如果想要在没有GC的情况下确保资源安全和异常安全,那么请使用智能指针:
    void fun4() {
        auto_ptr<file> spfile(new file("my.txt"));
         //操作文件
    } //此处,spfile结束生命周期的时候,会释放(delete)对象
    那么,智能指针又是怎么做到的呢?下面的代码告诉你其中的把戏(关于智能指针的更进一步的内容,请参考std::auto_ptr,boost或shared_ptr的智能指针)。
    也就是说,智能指针通过RAII来确保内存资源的安全,也间接地使得对象上的RAII得到实施。不过,这里的RAII并不是十分严格:对象(所占的内存也是资源)的创建(资源获取)是在构造函数之外进行的。广义上,我们也把它划归RAII范畴。
    但是,Matthew Wilson在《Imperfect C++》一书中,将其独立出来,称其为RRID(Resource Release Is Destruction)。
    RRID的实施需要在类的开发者和使用者之间建立契约,采用相同的方法获取和释放资源。比如,如果在shared_ptr构造时使用malloc(),便会出现问题,因为shared_ptr是通过delete释放对象的。

    noexcept异常说明及其使用

    noexcept异常使用

    相比于断言适用于排除逻辑上不可能存在的状态,异常通常是用于逻辑上可能发生的错误。在C++98中,我们看到了一套完整的不同于C的异常处理系统。通过这套异常处理系统,C++拥有了远比C强大的异常处理功能。

    在异常处理的代码中,程序员有可能看到过如下的异常声明表达形式:
    void excpt_func() throw(int, double) { ... }

    在excpt_func函数声明之后,我们定义了一个动态异常声明throw(int, double),该声明指出了excpt_func可能抛出的异常的类型。事实上,该特性很少被使用,因此在C++11中被弃用了(参见附录B),而表示函数不会抛出异常的动态异常声明throw()也被新的noexcept异常声明所取代。

    noexcept形如其名地,表示其修饰的函数不会抛出异常。不过与throw()动态异常声明不同的是,在C++11中如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()函数来终止程序的运行,这比基于异常机制的throw()在效率上会高一些。这是因为异常机制会带来一些额外开销,比如函数抛出异常,会导致函数栈被依次地展开(unwind),并依帧调用在本帧中已构造的自动变量的析构函数等。

    从语法上讲,noexcept修饰符有两种形式,一种就是简单地在函数声明后加上noexcept关键字。比如:

    void excpt_func() noexcept;

    另外一种则可以接受一个常量表达式作为参数,如下所示:
    void excpt_func() noexcept (常量表达式);

    常量表达式的结果会被转换成一个bool类型的值。该值为true,表示函数不会抛出异常,反之,则有可能抛出异常。这里,不带常量表达式的noexcept相当于声明了noexcept(true),即不会抛出异常。

    在通常情况下,在C++11中使用noexcept可以有效地阻止异常的传播与扩散。我们可以看看下面这个例子,如代码清单2-12所示。
    #include <iostream>
    using namespace std;
    void Throw() { throw 1; }
    void NoBlockThrow() { Throw(); }
    void BlockThrow() noexcept { Throw(); }
     
    int main() {
        try {
            Throw();
        }
        catch(...) {
            cout << "Found throw." << endl;     // Found throw.
        }
     
        try {
            NoBlockThrow();
        }
        catch(...) {
            cout << "Throw is not blocked." << endl;    // Throw is not blocked.
        }
     
        try {
            BlockThrow();   // terminate called after throwing an instance of 'int'
        }
        catch(...) {
            cout << "Found throw 1." << endl;
        }
    }

    修饰的函数。从main的运行中我们可以看到,NoBlockThrow会让Throw函数抛出的异常继续抛出,直到main中的catch语句将其捕捉。而BlockThrow则会直接调用std::terminate中断程序的执行,从而阻止了异常的继续传播。从使用效果上看,这与C++98中的throw()是一样的。

    而noexcept作为一个操作符时,通常可以用于模板。比如:
    template <class T>
    void fun() noexcept(noexcept(T())) {}

    这里,fun函数是否是一个noexcept的函数,将由T()表达式是否会抛出异常所决定。这里的第二个noexcept就是一个noexcept操作符。当其参数是一个有可能抛出异常的表达式的时候,其返回值为false,反之为true(实际noexcept参数返回false还包括一些情况,这里就不展开讲了)。这样一来,我们就可以使模板函数根据条件实现noexcept修饰的版本或无noexcept修饰的版本。从泛型编程的角度看来,这样的设计保证了关于“函数是否抛出异常”这样的问题可以通过表达式进行推导。因此这也可以视作C++11为了更好地支持泛型编程而引入的特性。

    虽然noexcept修饰的函数通过std::terminate的调用来结束程序的执行的方式可能会带来很多问题,比如无法保证对象的析构函数的正常调用,无法保证栈的自动释放等,但很多时候,“暴力”地终止整个程序确实是很简单有效的做法。事实上,noexcept被广泛地、系统地应用在C++11的标准库中,用于提高标准库的性能,以及满足一些阻止异常扩散的需求。

    比如在C++98中,存在着使用throw()来声明不抛出异常的函数

    template<class T> class A {
    public:
    static constexpr T min() throw() { return T(); }
    static constexpr T max() throw() { return T(); }
    static constexpr T lowest() throw() { return T(); }
    ...

    而在C++11中,则使用noexcept来替换throw()。

    template<class T> class A {
    public:
    static constexpr T min() noexcept { return T(); }
    static constexpr T max() noexcept { return T(); }
    static constexpr T lowest() noexcept { return T(); }
    ...

    又比如,在C++98中,new可能会包含一些抛出的std::bad_alloc异常。

    void* operator new(std::size_t) throw(std::bad_alloc);
    void* operator new[](std::size_t) throw(std::bad_alloc);

    而在C++11中,则使用noexcept(false)来进行替代。

    void* operator new(std::size_t) noexcept(false);
    void* operator new[](std::size_t) noexcept(false);

    当然,noexcept更大的作用是保证应用程序的安全。比如一个类析构函数不应该抛出异常,那么对于常被析构函数调用的delete函数来说,C++11默认将delete函数设置成noexcept,就可以提高应用程序的安全性。
    void operator delete(void*) noexcept;
    void operator delete[](void*) noexcept;

    而同样出于安全考虑,C++11标准中让类的析构函数默认也是noexcept(true)的。当然,如果程序员显式地为析构函数指定了noexcept,或者类的基类或成员有noexcept(false)的析构函数,析构函数就不会再保持默认值。我们可以看看下面的例子:
    #include <iostream>
    using namespace std;
     
    struct A {
        ~A() { throw 1; }
    };
     
    struct B {
        ~B() noexcept(false) { throw 2; }
    };
     
    struct C {
        B b;
    };
     
    int funA() { A a; }
    int funB() { B b; }
    int funC() { C c; }
     
    int main() {
        try {
            funB();
        }
        catch(...){
            cout << "caught funB." << endl; // caught funB.
        }
     
        try {
            funC();
        }
        catch(...){
            cout << "caught funC." << endl; // caught funC.
        }
     
        try {
            funA(); // terminate called after throwing an instance of 'int'
        }
        catch(...){
            cout << "caught funA." << endl;
        }
    }

    在代码中,无论是析构函数声明为noexcept(false)的类B,还是包含了B类型成员的类C,其析构函数都是可以抛出异常的。只有什么都没有声明的类A,其析构函数被默认为noexcept(true),从而阻止了异常的扩散。这在实际的使用中,应该引起程序员的注意。

    C++总结:C++中的const和constexpr

    const的语义

    C++中的const的目的是通过编译器来保证对象的常量性,强制编译器将所有可能违背const对象的常量性的操作都视为error。

    对象的常量性可以分为两种:物理常量性(即每个bit都不可改变)和逻辑常量性(即对象的表现保持不变)。C++中采用的是物理常量性,例如下面的例子:

    1
    2
    3
    4
    5
    6
    7
    struct A {
        int *ptr;
    };
    int k = 5, r = 6;
    const A a = {&k};
    a.ptr = &r; // !error
    *a.ptr = 7; // no error

    a是const对象,则对a的任何成员进行赋值都会被视为error,但如果不改动ptr,而是改动ptr指向的对象,编译器就不会报错。这实际上违背了逻辑常量性,因为A的表现已经改变了!

    逻辑常量性的另一个特点是,const对象中可以有某些用户不可见的域,改变它们不会违背逻辑常量性。Effective C++中的例子是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class CTextBlock {
    public:
        ...
        std::size_t length() const;
    private:
        char *pText;
        std::size_t textLength;            // last calculated length of textblock
        bool lengthIsValid;                // whether length is currently valid
    };

    CTextBlock对象每次调用length方法后,都会将当前的长度缓存到textLength成员中,而lengthIsValid对象则表示缓存的有效性。这个场景中textLength和lengthIsValid如果改变了,其实是不违背CTextBlock对象的逻辑常量性的,但因为改变了对象中的某些bit,就会被编译器阻止。C++中为了解决此问题,增加了mutable关键字。

    本部分总结:C++中const的语义是保证物理常量性,但通过mutable关键字可以支持一部分的逻辑常量性。

    const修饰变量

    如上节所述,用const修饰变量的语义是要求编译器去阻止所有对该变量的赋值行为。因此,必须在const变量初始化时就提供给它初值:

    1
    2
    3
    const int i;
    i = 5; // !error
    const int j = 10; // ok

    这个初值可以是编译时即确定的值,也可以是运行期才确定的值。如果给整数类型的const变量一个编译时初值,那么可以用这个变量作为声明数组时的长度:

    1
    2
    3
    4
    const int COMPILE_CONST = 10;
    const int RunTimeConst = cin.get();
    int a1[COMPLIE_CONST]; // ok in C++ and error in C
    int a2[RunTimeConst]; // !error in C++

    因为C++编译器可以将数组长度中出现的编译时常量直接替换为其字面值,相当于自动的宏替换。(gcc验证发现,只有数组长度那里直接做了替换,而其它用COMPILE_CONST赋值的地方并没有进行替换。)

    文件域的const变量默认是文件内可见的,如果需要在b.cpp中使用a.cpp中的const变量M,需要在M的初始化处增加extern:

    1
    2
    3
    4
    5
    //a.cpp
    extern const int M = 20;
     
    //b.cpp
    extern const int M;

    一般认为将变量的定义放在.h文件中会导致所有include该.h文件的.cpp文件都有此变量的定义,在链接时会造成冲突。但将const变量的定义放在.h文件中是可以的,编译器会将这个变量放入每个.cpp文件的匿名namespace中,因而属于是不同变量,不会造成链接冲突。(注意:但如果头文件中的const量的初始值依赖于某个函数,而每次调用此函数的返回值不固定的话,会导致不同的编译单元中看到的该const量的值不相等。猜测:此时将该const量作为某个类的static成员可能会解决此问题。)

    const修饰指针与引用

    const修饰引用时,其意义与修饰变量相同。但const在修饰指针时,规则就有些复杂了。

    简单的说,可以将指针变量的类型按变量名左边最近的‘*’分成两部分,右边的部分表示指针变量自己的性质,而左边的部分则表示它指向元素的性质:

    1
    2
    3
    4
    5
    6
    7
    8
    const int *p1; // p1 is a non-const pointer and points to a const int
    int * const p2; // p2 is a const pointer and points to a non-const int
    const int * const p3; // p3 is a const pointer and points to a const it
    const int *pa1[10]; // pa1 is an array and contains 10 non-const pointer point to a const int
    int * const pa2[10]; // pa2 is an array and contains 10 const pointer point to a non-const int
    const int (* p4)[10]; // p4 is a non-const pointer and points to an array contains 10 const int
    const int (*pf)(); // pf is a non-const pointer and points to a function which has no arguments and returns a const int
    ...

    const指针的解读规则差不多就是这些了……

    指针自身为const表示不可对该指针进行赋值,而指向物为const则表示不可对其指向进行赋值。因此可以将引用看成是一个自身为const的指针,而const引用则是const Type * const指针。

    指向为const的指针是不可以赋值给指向为非const的指针,const引用也不可以赋值给非const引用,但反过来就没有问题了,这也是为了保证const语义不被破坏。

    可以用const_cast来去掉某个指针或引用的const性质,或者用static_cast来为某个非const指针或引用加上const性质:

    1
    2
    3
    4
    int i;
    const int *cp = &i;
    int *p = const_cast<int *>(cp);
    const int *cp2 = static_cast<const int *>(p); // here the static_cast is optional

    C++类中的this指针就是一个自身为const的指针,而类的const方法中的this指针则是自身和指向都为const的指针。

    类中的const成员变量

     

    类中的const成员变量可分为两种:非static常量和static常量。

    非static常量:

    类中的非static常量必须在构造函数的初始化列表中进行初始化,因为类中的非static成员是在进入构造函数的函数体之前就要构造完成的,而const常量在构造时就必须初始化,构造后的赋值会被编译器阻止。

    1
    2
    3
    4
    5
    6
    7
    8
    class B {
    public:
        B(): name("aaa") {
            name = "bbb"; // !error
        }
    private:
        const std::string name;
    };

    static常量:

    static常量是在类中直接声明的,但要在类外进行唯一的定义和初始值,常用的方法是在对应的.cpp中包含类的static常量的定义:

    1
    2
    3
    4
    5
    6
    7
    8
    // a.h
    class A {
        ...
        static const std::string name;
    };
     
    // a.cpp
    const std::string A::name("aaa");

    一个特例是,如果static常量的类型是内置的整数类型,如char、int、size_t等,那么可以在类中直接给出初始值,且不需要在类外再进行定义了。编译器会将这种static常量直接替换为相应的初始值,相当于宏替换。但如果在代码中我们像正常变量那样使用这个static常量,如取它的地址,而不是像宏一样只使用它的值,那么我们还是需要在类外给它提供一个定义,但不需要初始值了(因为在声明处已经有了)。

    1
    2
    3
    4
    5
    6
    7
    8
    // a.h
    class A {
        ...
        static const int SIZE = 50;
    };
     
    // a.cpp
    const int A::SIZE = 50; // if use SIZE as a variable, not a macro

    const修饰函数

    C++中可以用const去修饰一个类的非static成员函数,其语义是保证该函数所对应的对象本身的const性。在const成员函数中,所有可能违背this指针const性(const成员函数中的this指针是一个双const指针)的操作都会被阻止,如对其它成员变量的赋值以及调用它们的非const方法、调用对象本身的非const方法。但对一个声明为mutable的成员变量所做的任何操作都不会被阻止。这里保证了一定的逻辑常量性。

    另外,const修饰函数时还会参与到函数的重载中,即通过const对象、const指针或引用调用方法时,优先调用const方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class A {
    public:
        int &operator[](int i) {
            ++cachedReadCount;
            return data[i];
        }
        const int &operator[](int i) const {
            ++size; // !error
            --size; // !error
            ++cachedReadCount; // ok
            return data[i];
        }
    private:
        int size;
        mutable cachedReadCount;
        std::vector<int> data;
    };
     
    A &a = ...;
    const A &ca = ...;
    int i = a[0]; // call operator[]
    int j = ca[0]; // call const operator[]
    a[0] = 2; // ok
    ca[0] = 2; // !error

    这个例子中,如果两个版本的operator[]有着基本相同的代码,可以考虑在其中一个函数中去调用另一个函数来实现代码的重用(参考Effective C++)。这里我们只能用非const版本去调用const版本。

    1
    2
    3
    int &A::operator[](int i) {
        return const_cast<int &>(static_cast<const A &>(*this).operator[](i));
    }

    其中为了避免调用自身导致死循环,首先要将*this转型为const A &,可以使用static_cast来完成。而在获取到const operator[]的返回值后,还要手动去掉它的const,可以使用const_cast来完成。一般来说const_cast是不推荐使用的,但这里我们明确知道我们处理的对象其实是非const的,那么这里使用const_cast就是安全的。

    constexpr

    constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。

    constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。

    1
    2
    3
    4
    5
    6
    7
    constexpr int Inc(int i) {
        return i + 1;
    }
     
    constexpr int a = Inc(1); // ok
    constexpr int b = Inc(cin.get()); // !error
    constexpr int c = a * 2 + 1; // ok

    constexpr还能用于修饰类的构造函数,即保证如果提供给该构造函数的参数都是constexpr,那么产生的对象中的所有成员都会是constexpr,该对象也就是constexpr对象了,可用于各种只能使用constexpr的场合。注意,constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中。

    1
    2
    3
    4
    5
    6
    7
    struct A {
        constexpr A(int xx, int yy): x(xx), y(yy) {}
        int x, y;
    };
     
    constexpr A a(1, 2);
    enum {SIZE_X = a.x, SIZE_Y = a.y};

    constexpr的好处:

    1. 是一种很强的约束,更好地保证程序的正确语义不被破坏。
    2. 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
    3. 相比宏来说,没有额外的开销,但更安全可靠。
    展开全文
  • 戟:一个constexpr词法分析器
  • constexpr变量 constexpr表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。声明为constexpr的变量一定是一个const变量,而且必须用常量表达式初始化: constexpr int mf = 20; //20是常量表达式 ...

    constexpr变量

    • constexpr表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。声明为constexpr的变量一定是一个const变量,而且必须用常量表达式初始化:
    constexpr int mf = 20;  //20是常量表达式
    constexpr int limit = mf + 1; // mf + 1是常量表达式
    constexpr int sz = size(); //只有当size是一个constexpr函数时才是一条正确的声明语句

    指针和constexpr

    • 必须明确一点,在constexpr声明中如果定义了一个指针,限定符conxtexpr仅对指针有效,与指针所指的对象无关。
    const int*p = nullptr;        //p是一个指向整形常量的指针
    constexpr int* q = nullptr;   //q是一个指向整数的常量指针

    p是一个指向常量的指针,q是一个常量指针,其中的关键在于constexpr把它所定义的对象置为了顶层const。

    例在微软编译器上:

    #include <iostream>
    int main()
    {
        int i = 10;
        std::cout << "i=" << i << std::endl;
        
        constexpr int* p = &i;
        *p = 8;
        std::cout << "i=" << i << std::endl;
     
        return 0;
    }

    结果:i=10;1=8;

    constexpr函数

    • constexpr函数是指能用于常量表达式的函数。该函数要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句:
    constexpr int new_sz() {return 42;}
    constexpr int foo =new_sz();		//正确:foo是一个常量表达式
    • 在对变量foo初始化时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数
    • constexpr函数体内也可以包含其它语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明。

    需要注意的是,我们允许constexpr函数的返回值并非一个常量:

    //如果arg是常量表达式,则scale(arg)也是常量表达式
    constexpr size_t scale(size_t cnt){ return new_sz() * cnt; }

    当scale的实参是常量表达式时,它的表达式也是常量表达式,反之则不然:

    int arr[scale(2)];    //正确:scale(2)是常量表达式
    int i = 2;            //i不是常量表达式
    int a2[scale(i)];    //错误:scale(i)不是常量表达式

    当把scale函数用在需要常量表达式的上下文中时,如果其结果恰好不是常量表达式,编译器将发出错误信息。

    • 注意,我们要把内联函数和constexpr函数定义在头文件中。因为内链函数是内部链接的,如果你在b.cpp中定义这个函数,那么在a.cpp中即使有这个函数声明,但由于内联函数是内部链接的,所以b.cpp不会提供其定义。所以在链接时a.obj无法找到这个函数的定义,便会出现无法解析的外部符号的错误

     

    展开全文
  • constexpr-nn 这是什么? 这是一个使用C ++ 17 constexpr在编译时计算神经网络(MLP)反向传播的程序。 你为什么要那么做? 很有趣。 为什么您实际上要这么做? 其乐无穷。 要求 C ++ 17编译器 修补叮当声是最好...
  • 变量:使用 constexpr 修饰的变量,编译器确保在编译期而非运行期确保该变量为常量。达到清晰、明确以及提高性能的效果。 constexpr int a = 1; constexpr int b = a + 1; 函数:返回类型及所有形参的类型都...
  • 使用constexpr进行实验 一切(除注明的功能外)均以C ++ 11 constexpr样式编写,以实现最大的兼容性。 所有功能都在cx名称空间内。 该代码在MIT许可下分发。 有关详细信息,请参见许可。 数学函数 abs , fabs ...
  • 2.12 使用const比使用define有一下几种好处 2.13 const与动态分配 3.constexpr关键字 3.1 constexpr变量 3.2 constexpr函数 4.volatile关键字 4.1 volatile定义 4.2 volatile与指针 4.3 volatile与多线程 4.4 ...
  • C++11关键字constexpr看这篇就够了

    千次阅读 2020-10-26 23:50:46
    constexpr关键字可以解决这种问题,在GetLen函数前加constexpr声明,代码如下: 当然,constexpr修饰的函数也有一定的限制: (1)函数体尽量只包含一个return语句,多个可能会编译出错; ...
  • C++中的constexpr的意义与用法

    千次阅读 2020-01-06 10:30:58
    以下内容转载medium, 字体为繁体, 不喜欢勿入, 无奈...内容转载https://medium.com/@tjsw/%E6%BD%AE-c-constexpr-constructor-constexpr-operator-overloading-3a11062900ff Part1 近代的 C++ 中為我們傳統熟悉...
  • 文章目录尽可能使用 `const`参数例外返回值例外`const this` 和成员`const_cast`与 `constexpr` 的关系函数变量构造函数 尽可能使用 const C++ 与 C 语言相比有着更强的类型检查,包括四种 cast,左值右值之分,...
  • C++总结:C++中的const和constexpr 2014-02-18 15:31付哲 阅读(14334) 评论(0)编辑收藏 C++中的const可用于修饰变量、函数,且在不同的地方有着不同的含义,现总结如下。 const的语义 C++中的const的目的是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,854
精华内容 5,941
关键字:

constexpr