C++Primer:字面值常量类调用函数错误(p268书中示例报错)


1. 背景

  在阅读到C++Primer第268页时发现其示例编写好后程序报错。

#include <iostream>
using namespace std;
class Debug
{
public:
    constexpr Debug(bool b = true) : hw(b), io(b), other(b){};
    constexpr Debug(bool h, bool i, bool o) : hw(h), io(i), other(o){};
    constexpr bool any()
    {
        return hw | io | other;
    }
    void set_io(bool b)
    {
        io = b;
    }
    void set_hw(bool b)
    {
        hw = b;
    }
    void set_other(bool b)
    {
        other = b;
    }

private:
    bool hw;
    bool io;
    bool other;
};

int main()
{
    constexpr Debug io_sub(false, true, false);
    if(io_sub.any())
    {
        cerr << "print approptiate error messages" << endl;
    }
    constexpr Debug prod(false);
    if(prod.any())
    {
        cerr << "print an error message" << endl;
    }
    return 0;
}

报错提示

2. 基础相关知识(可跳过)

  1. 常量表达式。值不会改变并且在编译过程就能得到计算结果的表达式。
const int max_files = 20; // max_files是常量表达式
const int limit = max_files + 1; // limit是常量表达式
int staff_size=27; // staff_size不是常量表达式,因为staff_size不是常量
const int sz = get_size(); // sz不是常量表达式,因为get_size()结果未知
  1. constexpr变量。声明为constexpr的变量一定是一个常量,且必须用常量表达式初始化。
constexpr int mf = 20;
constexpr int limit = mf + 1;
const int *p1 = nullptr; //p1是指向常量的指针
int * const p2; //p2本身是常量
constexpr int *p3 = nullptr; // p3本身也是常量
  1. 字面值类型。算术类型、引用和指针属于字面值类型,自定义类、IO库、string类型不属于字面值类型。只有字面值类型才能声明constexpr。

  2. constexpr函数。能用于常量表达式的函数。其函数的返回类型和所有形参的类型都是字面值类型,函数体中必须有且只有一条return语句。constexpr函数不一定返回常量表达式。

constexpr int new_sz() {return 42;}
constexpr int foo= new_sz();
  1. 聚合类。当一个类满足以下条件时,该类便是聚合类:所有成员都是public;没有定义任何构造函数;没有类内初始值;没有基类和virtual函数。

  2. 字面值常量类。数据成员都是字面值类型的聚合类是字面值常量类,除此以外,满足以下所有条件的也是字面值常量类:数据成员都是字面值类型;类必须至少含有一个constexpr构造函数;若一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式,或者若成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数; 类必须使用析构函数的默认定义,该成员负责销毁类的对象。

3. 分析

  1. 由示例可知,io_sub和prod是constexpr变量。故io_sub和prod本身便是常量,其类型相当于const Debug
//constexpr Debug等价于 const Debug 
constexpr Debug io_sub(false, true, false);
constexpr Debug prod(false);
  1. 成员函数any()是一个constexpr函数。在c++11标准下,constexpr函数是常量成员函数,在c++14标准下,constexpr函数不是常量成员函数。我的编译器默认constexpr函数不是常量成员函数。
constexpr bool any()
//c++11标准下等价于
bool any () const
//c++14标准下等价于
bool any()
  1. 常量对象io_sub在调用成员函数any()时,this=&io_sub。this的类型是Debug * const,指向Debug,而io_sub是const Dedug。一般指针的类型必须与其所指向对象的类型相匹配,故调用错误。prod同理也是如此。
io_sub.any()
prod.any()

4. 改正方法

  既然在默认c++标准下,constexpr函数不是常量成员函数,而io_sub和prod是常量对象,只能调用常量成员函数。那可通过以下2种方式将any()修改为常量成员函数:

  1. 在any()后加const使其成为常量成员函数。
constexpr bool any() const
{
	return hw | io | other;
}

运行结果

  1. 编译时使用c++11标准,是使constexpr函数默认是常量成员函数。

运行结果

5. 总结

  1. 声明为constexpr的变量一定是一个常量。
  2. 类中声明为constexpr的函数不一定是常量成员函数,这由编译的c++标准决定。编程时,最好根据自己需要主动添加const。
  3. 构造函数不能是常量成员函数,但字面值常量类的构造函数可以是constexpr函数。
发布了77 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_34801642/article/details/104634709