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 &),
编译器背后的工作
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 在标准库中的使用
=default 在标准库中的使用
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 的。
这些函数式做了什么呢?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 吗?
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
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;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。