2

explicit

explicit for ctors taking one argument

  • 此关键字是一个声明说明符,只能应用于类内构造函数声明。 显式构造函数不能参与隐式转换

文件:Test.cpp

#include <iostream>

using namespace std;

struct Complex
{
    int real, imag;

    Complex(int re, int im = 0) : real(re), imag(im)
    {
        cout << "re=" << re << "; im=" << im << endl;
    }

    Complex operator+(const Complex &x)
    {
        cout << "Complex operator+(const Complex &x)" << endl;

        return Complex(real+x.real, imag+x.imag);
    }
};

int main()
{
    Complex c1(12, 5);
    Complex c2 = c1 + 5;  // 注意这里!!

    return 0;
}

输出:

re=12; im=5
re=5; im=0
Complex operator+(const Complex &x)
re=17; im=5
  • 说明:Complex c2 = c1 + 5;

编译器查找是否有函数可以被匹配调用,查找得到类内自定义实现 ' operator + ',但参数类型为 'const Complex &x',于是发生隐式类型转换 '5 => Complex(5, 0)' 得到临时对象。


文件:Test.cpp

#include <iostream>

using namespace std;

struct Complex
{
    int real, imag;

    explicit Complex(int re, int im = 0) : real(re), imag(im)
    {
        cout << "re=" << re << "; im=" << im << endl;
    }

    Complex operator+(const Complex &x)
    {
        cout << "Complex operator+(const Complex &x)" << endl;

        return Complex(real+x.real, imag+x.imag);
    }
};

int main()
{
    Complex c1(12, 5);
    Complex c2 = c1 + 5;  // 注意这里!!

    return 0;
}

编译输出:

error: no match for ‘operator+’ (operand types are ‘Complex’ and ‘int’)

explicit for ctors taking more than one argument

文件:Test.cpp

#include <iostream>

using namespace std;

class P
{
public:

    P(int a, int b, int c)
    {
        cout << "explicit P(int a, int b, int c)" << endl;
    }
};

void func(const P &obj)
{
    cout << "void func(class P &obj)" << endl;
}

int main()
{
    func({47, 11, 3});

    return 0;
}

输出:

explicit P(int a, int b, int c)
void func(class P &obj)

文件:Test.cpp

#include <iostream>

using namespace std;

class P
{
public:

    explicit P(int a, int b, int c)
    {
        cout << "explicit P(int a, int b, int c)" << endl;
    }
};

void func(const P &obj)
{
    cout << "void func(class P &obj)" << endl;
}

int main()
{
    func({47, 11, 3});

    return 0;
}

编译输出:

error: converting to ‘const P’ from initializer list would use explicit constructor ‘P::P(int, int, int)’

range-based for statement

  • 范式
for (decl/* 声明 */ : coll /* 容器 */)
{
    // ...
}
  • 示例
for (int i : {2, 4, 6, 8, 10} /* initializer_list<int> */)
{
    cout << i << endl;
}
vector <double> vec{2, 4, 6};

for (auto ele : vec)
{
    cout << elem << endl;
}

for (auto &elem : vec)
{
    elem *= 3;
}
  • 注:auto &elem 的使用情况

当需要修改容器内数据时;
非基础数据类型时, & 避免拷贝构造,更为高效(不需要修改原数据时:const auto &),

编译器背后的工作

1_meitu_1.jpg

2_meitu_2.jpg


for + explicit

no explicit type conversions are possible when are initialized as declare inside the for loop, Thus, the following does not compile.

(当在for循环中以声明的形式初始化时,不可能进行显式类型转换,因此,下面的代码不能编译。)

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class C
{
public:
    explicit C(const string &s) // explicit(!) type conversion from strings
    { }
    // ...
};

int main()
{
    vector<string> vs;

    for (const C &elem : vs)
    {
        // ...
    }

    return 0;
}

编译输出:

error: invalid initialization of reference of type ‘const C&’ from expression of type ‘std::__cxx11::basic_string<char>’

=default && =delete

  • 如果自定义了一个构造函数,那么编译器不会再生成任何一个default ctor。
  • 如果强加上 =default,就可以重新获得并使用 default ctor。
class Zoo
{
public:
    Zoo(int i1, int i2) : d1(i1), d2(i2) { }
    Zoo(const Zoo&) = delete ;
    Zoo(Zoo&&) = default;
    Zoo& operator=(const Zoo&) = default;
    Zoo& operator=(Zoo&&) = delete;
    virtual ~Zoo() { }

private:
    int d1;
    int d2;
};

class Foo
{
public:
    Foo(int i) : _i(i) {}
    Foo() = default;

    Foo(const Foo &x) : _i(x._i) {} 
    // Foo(const Foo&) = default;  //  error: ‘Foo::Foo(const Foo&)’ cannot be overloaded
   // Foo(const Foo&) = delete;    // error: ‘Foo::Foo(const Foo&)’ cannot be overloaded

    Foo& operator=(const Foo &x) {_i = x._i; return *this;}
    // Foo& operator=(const Foo &x) = default;  // error: ‘Foo& Foo::operator=(const Foo&)’ cannot be overloaded
    // Foo& operator=(const Foo &x) = delete;   // error: ‘Foo& Foo::operator=(const Foo&)’ cannot be overloaded

    // void func1() = default;  // error: ‘void Foo::func1()’ cannot be defaulted
    void func2() = delete;

    // ~Foo() = delete;  // 编译通过。这可能会造成使用 Foo object 时出错 =>   error: use of deleted function ‘Foo::~Foo()’
    ~Foo() = default;

private:
    int _i;
};
  • 注: 【详细阅读以上注释信息】

= default 用于 Big-Five 之外时什么意义? => 无意义,编译报错;
= delete 可用于任何函数上。(=0只能用于虚函数且不可与delete 同时使用)

  • Big-Five
class C 
{
  C(const C&) = default;             // Copy constructor
  C(C&&) = default;                  // Move constructor
  C& operator=(const C&) = default;  // Copy assignment operator
  C& operator=(C&&) = default;       // Move assignment operator
  virtual ~C() { }                   // Destructor
};

=delete 在标准库中的使用

3_meitu_3.jpg

=default 在标准库中的使用

4_meitu_4.jpg

5_meitu_5.jpg

know what functions C++ silently writes and calls

  • 问题:什么时候 empty class 不再是 empty 呢?

当C++处理过它之后。
如果没有自己声明,编译器就会为它声明一个 copy ctor、一个 copy assignment operator 和 一个 dtor (都是所谓的 synthesized version(合成版本))。
如果没有声明任何 ctor, 编译器也会声明一个 default ctor。
这些函数式都是 public 且 inline 的。

6_meitu_6.jpg

这些函数式做了什么呢?default ctor 和 dtor 主要给编译器一个地方用来放置[藏身幕后]的code,像唤是起 base classes 以及 non-static members 的 ctors 和 dtors。

编译器产出的 dtor 是 non-virtual, 除非这个 class 的 base class 本身宣告有 virtual dtor。

至于 copy ctor 和 copy assignment operator, 编译器合成的只是单纯的将 source object 的每一个 non-static data members 拷贝到 destination object。

complex<T> 有所谓的 Big-Three 吗?

7_meitu_7.jpg

string 有所谓的 Big-Three 吗?

有,而且有 Big-Five.

No-Copy and Private-Copy

struct NoCopy
{
    NoCopy() = default;                             // use the synthesized default construct
    NoCopy(const NoCopy&) = delete;                 // no copy construct
    NoCopy &operator=(const NoCopy&) = delete;      // no assignment operator
    ~NoCopy() = default;                            // use the synthesized destructor
    // other members
};
struct NoDtor
{
    NoDtor() = default; // use the synthesized default constructor
    ~NoDtor() = delete; // whe can't destroy object of type NoDot
};

// 注意这里!!
// NoDtor nd;  // error: use of deleted function ‘NoDtor::~NoDtor()’

NoDtor *p = new NoDtor();

// 注意这里!!
// delete p;           // error: use of deleted function ‘NoDtor::~NoDtor()’

=delete 告诉编译器不要定义它。必须出现在声明式。适用任何成员函数。但若用于 dtor 后果自负。

class PrivateCopy
{
private:
    // no access specifier, following members are private by default
    // copy control is private and so is inaccessible to ordinary user code
    PrivateCopy(const PrivateCopy&);
    PrivateCopy& operator=(const PrivateCopy&);
    // other members

 public:
       PrivateCopy() = default;     // use the synthesized default construct 
       ~PrivateCopy();                      // users can define objects of this type but not copy them
};

此 class 不允许被 ordinary user code copy, 但仍可被 friends 和 members copy。若欲完全禁止,不但必须把 copy controls 放到 private 内且不可以定义它。

bost::nonocopyable

8_meitu_8.jpg

Alias Template (template typdef)

template <typename T>
using Vec = std::vector<T, MyAlloc<T>>;  // standard vector using own allocator

==>

Vec<int> coll;

// [等价于]  <==> 

std::vector<int, MyAlloc<int>> coll;
  • 使用宏无法达到相同的效果
#define Vec<T> template<typename T> std::vector<T, MyAlloc<T>>

// 于是

Vec<int> coll;
Vec<int>
==>
template<typename int> std::vector<int, MyAlloc<int>> // 这不是我们想要的!!
  • 使用 typedef 无法达到相同的效果(因为 typedef 不接受任何参数)。
typedef std::vector<int, MyAlloc<int>> Vec;  // 这不是我们想要的!!

难道 using 只是为了少打几个字母?

问题:编写一个函数或类,使其内部可定义外部任意指定(指定方式不限)的容器类型和容器内元素类型组合而成的对象。
  • 尝试一【依据:函数模板可以进行类型推导】
template <typename Container, typename T>
void func(Container cnt, T elem)
{
    Container<T> c;

    //  ...
}

编译输出:

error: ‘Container’ is not a template
  • 尝试二
template <typename Container, typename T>
void func(Container cnt, T elem)
{
    typename Container<T> c;

    //  ...
}

编译输出:

error: expected nested-name-specifier before ‘Container’
间接思考:有没有一种语法能够在模板接受一个template参数 Container 时, 当 Container 本身又是一个 class template, 能取出 Container 的模板参数?例如收到一个vector<string>, 能够取出其元素类型 string ?
  • 尝试三【依据:iterator + traits 标准库特性】
template <typename Container>
void func(Container c)
{
    typedef typename iterator_traits<typename Container::iterator>::value_type Valtype;  // 注意这里!!

    Container cc {Valtype()} ;

    // ...
};

//--------

func(vector<string>());

编译输出:无错误、无警告。

另外思考:既然是 "Container", 其 iterator 就能够回答 value_type 这种问题,间接解决了上述提问。但依赖标准库特性,那在没有 iterator 和 traits 的情况下如何处理呢?
  • 尝试三【依据:template template parameter】
template <typename T, template <typename> class Container >  // 注意这里!!
class Func
{
private:
    Container<T> c { T() };

public:
    Func()
    {  }
    // ..
};

//--------

func(vector<string>());

编译输出:【模板的二次编译时出错】

error: type/value mismatch at argument 2 in template parameter list for ‘template<class T, template<class> class Container> class Func’

expected a template of type ‘template<class> class Container’, got ‘template<class _Tp, class _Alloc> class std::vector’

分析:
文件:stl_vector.h

vector 实现
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
    // ...
}

根据编译错误提示,可知问题在 std::vector,与单参数<T>期望不否。
可是 vector 第二个模板参数有指定默认值,那为什么还会报错呢?是因为,当作为模板模板参数进行传递时,编译器无法再完成模板模板参数默认值的推导,即 typename _Alloc = std::allocator<_Tp> 编译器无法推导得出,需要手动指定。

  • 尝试四 - 理想答案【依据:template template parameter + alias template】
template <typename T>
using Vec = vector<T, allocator<T>>;

template <typename T>
using Lst = list<T, allocator<T>>;

template <typename T, template <class> class Container >  
class Func
{
private:
    Container<T> c { T() };

public:
    Func()
    {  }
    // ..
};

//--------------

Func<string, Vec> c1;
Func<int, Lst> c2;

编译输出:无错误、无警告

Type Alias

There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.

(类型别名声明和typedef声明之间没有区别。 该声明可能出现在块作用域,类作用域或名称空间作用域中。)

// type alias, idential to
// tyodef void (*func)(int, int);
// <<==>>
using func = void(*)(int, int);

// the name 'func' now denote a pointer to function:
void example(int, int)
{ }

func fn = example;

// type alias can introduce a member typedef name
// (类型别名可以引入成员typedef名称)
template <typename T>
struct Container
{
    using value_type = T; // <==> typedef T value_type;
};

// type alias can introduce a member typedef name
// (类型别名可以引入成员typedef名称)
template <typename T>
struct Container
{
    using value_type = T; // <==> typedef T value_type;
};

//-----

template <typename Cntr>
void fn2(const Cntr &c)
{
    typename Cntr::value_type n;  // 注意这里!!
}

类型别名,用于隐藏模板参数
template <class CharT>
using mystring = std::basic_string<CharT, std::char_traits<CharT>>;

如, <string> 和 <string_fwd.h> 都有以下 typdef :
typedef basic_string<char> string;


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧