几个关键字


有几个关键字要么是记不住(很少用)要么就是弄混淆,它们就是explicitmutableconstexprconst

explicit

顾名思义,不是implicit。用在哪儿呢?

#include <iostream>

class T final
{
    public:
        T(std::string s) : str(s) {}
        ~T() = default;
        std::string &get()
        {
            return str;
        }
    private:
        std::string str;
};

void print(T t)
{
    std::cout << t.get() << std::endl;
}

int main()
{
    std::string s("this");
    print(s);
}

运行结果:

╭─angel at Chameleon in ~ using
╰─○ cll explicit.cpp && ./a.out 
this
this

事与愿违,我想的是main函数中明显是错误的,为什么还能运行呢?print接受一个class T然而我传给它一个string。避免这类问题,就需要使用explicit来告知编译器,你不能隐式的进行转换啊!

下面,为构造函数添加explicit

explicit T(std::string s) : str(s) {}

编译:

 cll explicit.cpp && ./a.out
explicit.cpp:35:5: error: no matching function for call to 'print'
    print(s);
    ^~~~~
explicit.cpp:27:6: note: candidate function not viable: no known conversion from
      'std::string' (aka 'basic_string<char>') to 'T' for 1st argument
void print(T t)
     ^
1 error generated.

这就和我们的预期一样了!还有个用到explicit的地方,就具体看这里

ps.另一个常见的是shared_ptrunique_ptr的构造函数也是explicit的,因此

shared_ptr<int> ptr = new int(309);

是错误的。

mutable

说实话,如果不是在用lambda表达式的话我甚至不知道有这个关键字。。。

#include <iostream>

int main()
{
    int x = 10;
    auto lambda = [x]() {
        x -= 1;
    };

    lambda();
    std::cout << x << std::endl;
}

错误:

cll mut.cpp
mut.cpp:14:11: error: cannot assign to a variable captured by copy in a non-mutable lambda
        x -= 1;
        ~ ^
1 error generated.

要想编译通过,可以使用引用捕获(如果真的需要改变这个值),另一种就是使用mutable(只是想临时的改变下这个值)

#include <iostream>

int main()
{
    int x = 10;
    auto lambda = [x]() mutable {
        x -= 1;
        std::cout << x << std::endl;
    };

    lambda();
    std::cout << x << std::endl;
}

输出:

cll mut.cpp && ./a.out
9
10

可以看到,只是临时的改变了x拷贝捕获的值。还有个作用见这里

constexpr

constptr可用来让表达式核定于编译期。
下面的代码是不能通过编译的

#include <iostream>

int power(int x)
{
    return x * x;
}

int main()
{
    int x[power(10)] = {0};
}

原因是

cll x.cpp
x.cpp:18:11: error: variable-sized object may not be initialized
    int x[power(10)] = {0};
          ^~~~~~~~~
1 error generated.

但是通过使用constexpr来修饰power()将代码变成这样

constexpr int power(int);

便能通过编译了。
此外还有__constexpr variable__以及__constexpr constructor__详见这里

const

顶/底层const

以下这段话摘自'C++ Primer (5th Edition)'

As we've seen, a pointer is an object that can point to a different object. As a result, we can talk independently about whether a pointer is const and whether the object to which it can point is const. We use the term top-level const to indicate that the pointer itself is a const, when a pointer can point to a const object, we refer to that const as a low-level const

几个例子

  1. const int i = 0int const i = 0 i自身不可变,顶层const。

  2. const int *p = &iint const *p = &i p指向的对象(i)不可变,底层const,此外int *p = &i是错误的,因为右值(&i)的类型是const int *

  3. int i = 0; const int *p = &i (或 int const *p = &i); p指向的对象不可变(*p = 1, 类似于1),底层const,由于p为指针,所以此例中p可以指向其他对象,比如int j = 0; p = &j

  4. int *const p = &i 和3相反,p不能指向其它对象,但是可以改变p所指向对象的值,所以这个const是顶层const。

  5. const int *const p = &i 左边的const是底层const,右边的则是顶层const,既不能改变p指向的对象的值,也不能使p指向其它对象

  6. const int i = 0; const int &x = i; 用于声明引用的const都是底层const。

const函数

函数参数是const

int foo(const int x) { return x; }
//或
int foo(const int &x) { return x; }

传入参数不要求是const的,在函数体中不能更改x的值,相当于顶层const。

int foo(const int *x) { return *x; }
//或
int foo(const int x[]) { return *x; }

传入参数必须是const的,在函数体中不能改变*x的值,当可以改变x指向的对象,和3一样。

int foo(const int *const x) { return *x; }

传入的参数必须是const的,和5相同。

struct Foo
{
  int i;
  Foo(const int &x) : i(x) {}
};

int main()
{
  int x = 0;
  Foo X = x;
}

复制构造函数可以提供隐式的转换,上面将x转换成了Foo,可以通过在构造函数前加explicit关键字禁止这种转化。

函数返回值是const

const int foo(int x) { return x; }

不算错,但没有作用。

const int* foo(int *x) { return x; }

返回值是只读的,但可以指向其它对象,和3相同。

const int& foo(int &x) { return x; }

返回值不能是一个右值,返回值不可变++foo(x)是错误的。但是返回值可以赋值给另一个左值,且这个左值不必是const的,但要求其提供了复制构造函数或是隐/显式的转换。比如int x = 1; int res = foo(x); res = 2;是可以的。

const类
class Foo
{
  public:
    mutable int x;
    Foo(): x(0) {}

    int get()
    {
      std::cout << "non-const\n";
      return x;
    }

    int get() const
    {
      std::cout << "const\n";
      return x;
    }
};

int main()
{
  const Foo foo;
  Foo bar;

  foo.get();
  bar.get();
}

打印出

cll t.cpp && ./a.out 
const
non-const

const对象,指向const对象的指针或引用只能调用其const成员函数,且在const成员函数中不能更改成员变量,比如:

class Foo
{
    int get() const
    {
       ++x; // 错误!
       return x;
    }
};

但是可以通过使用mutable来突破这一限制

class Foo
{
  public:
    mutable int x;
    Foo(): x(0) {}

    int get()
    {
      std::cout << "non-const\n";
      ++this->x;
      return x;
    }

    int get() const
    {
      std::cout << "const\n";
      ++this->x;
      return x;
    }
};

int main()
{
  const Foo foo;
  Foo bar;

  std::cout << foo.get() << std::endl;
  std::cout << bar.get() << std::endl;
}

输出

cll t.cpp && ./a.out
const
1
non-const
1

注意 在非const成员函数中,this指针是一个类类型;在const成员函数中,this指针是一个const类类型。



转载请注明:Serenity » 几个关键字

上一篇

下一篇