VC++ 2013现在支持原始字符串字面值了。注意:它并不支持统一码字符串字面值。一个原始字符串字面值允许你避免转义那些在HTML,XML和正则表达式里运用得得心应手的特殊字符。下面是一个示例用法:
auto s1 = R"(This is a "raw" string)";现在,s1是一个指向常量字符串值为“This is a "raw" string”的指针。尽管不支持嵌套双引号,这与C#支持的@string文字是类似的。那么要在一个字符串字面值中嵌入R"(...)"会怎样。这种情况下,你可以使用以下语法:
auto s2 = R"QQ(Example: R"(This is my raw string)")QQ";现在,s2包含 Example: R"(This is my raw string)"。 在这个例子中,我把QQ作为界定符。这个界定符可以是任何长度不超过16的字符串。原始字符串字面值也可以包含换行:
auto s3 = R"(<tr> <td>data</td> </tr>)";最后,不论他们什么时候添加统一码字符串字面值的支持,你都可以将它们连接起来并构成原始统一码字符串字面值。
可变参数模板是一个允许多个参数的模板。在我看来,这是个提供给库作者而不是给库使用者的特性,所以我也不是很确定它在C++程序员中会有多流行。以下我们用一个非常简单的例子来展示如何在实际开发中使用可变参数模板。
// Variadic template declaration template<typename... Args> class Test; // Specialization 1 template<typename T> class Test<T> { public: T Data; }; // Specialization 2 template<typename T1, typename T2> class Test<T1, T2> { public: T1 Left; T2 Right; }; void Foo() { Test<int> data; data.Data = 24; Test<int, int> twovalues; twovalues.Left = 12; twovalues.Right = 15; }
当使用可变参数模板时,智能感应(intellisense)能很好地配合我们的开发。可变参数模板的实现包括一个叫asizeof的函数,这个函数能返回这个模板的参数个数。
template<typename... Args> class Test { public: size_t GetTCount() { return sizeof...(Args); } }; // . . . Test<int> data; size_t args = data.GetTCount(); //1 Test<int, int, char*> data2; args = data2.GetTCount(); //3 Test<int, float> data3; args = data3.GetTCount(); //2
这其实就是一个数个数的例子,但我猜他们之所以使用一个现存的函数名是因为这样子做会让C++程序员们更容易上手。
对于可变参数模板,一个常用的做法就是专攻其中一个参数,然后把其余的参数都变为可选。这个做法可以以递归的形式实现。以下是一个比较傻的例子,但它能让你明白什么时候不应该用可变参数模板,继而更好地了解这个语言特性。
template<typename... Args> class Test; // Specialization for 0 arguments template<> class Test<> { }; // Specialization for at least 1 argument template<typename T1, typename... TRest> class Test<T1, TRest...> : public Test<TRest...> { public: T1 Data; // This will return the base type Test<TRest...>& Rest() { return *this; } }; void Foo() { Test<int> data; data.Data = 24; Test<int, int> twovalues; twovalues.Data = 10; // Rest() returns Test<int> twovalues.Rest().Data = 11; Test<int, int, char*> threevalues; threevalues.Data = 1; // Rest() returns Test<int, int> threevalues.Rest().Data = 2; // Rest().Rest() returns Test<char*> threevalues.Rest().Rest().Data = "test data"; }
大家请注意了,千万别把代码写成这样。这个例子仅仅是用来教学的,正确的做法我会在下一个章节中告诉大家。
我们来看一下std tuple的头文件 (由VC++团队的Stephan T. Lavavej负责维护 - 最初的代码由P.J. Plauger编写),浏览这些代码,让我的大脑几乎要宕掉了。为了更好的理解代码,我将代码进行简化,摘出其中可以访问tuple的值的最少的代码(能够支持读和写)。这有助于理解在设计模板类时,通常可变参数模板是如何通过递归展开来大幅减少代码的行数。
// tuple template<class... _Types> class tuple; // 空tuple template<> class tuple<> {}; // 递归的tuple定义 template<class _This, class... _Rest> class tuple<_This, _Rest...> : private tuple<_Rest...> { public: _This _Myfirst; };
// tuple_element template<size_t _Index, class _Tuple> struct tuple_element; // select first element template<class _This, class... _Rest> struct tuple_element<0, tuple<_This, _Rest...>> { typedef _This& type; typedef tuple<_This, _Rest...> _Ttype; }; // recursive tuple_element definition template <size_t _Index, class _This, class... _Rest> struct tuple_element<_Index, tuple<_This, _Rest...>> : public tuple_element<_Index - 1, tuple<_Rest...> > { };这里又一次使用了递归继承,同时边界条件也特化了。注意这里的两个typedef,其中一个定义为对应值类型的引用,另一个定义为和tuple_element类型参数相同的tuple类型。因此,给定一个_Index值,在那个递归层次上我们就能得到对应tuple的类型和tuple值的类型。下面的get方法就使用了这个特性。
// get reference to _Index element of tuple template<size_t _Index, class... _Types> inline typename tuple_element<_Index, tuple<_Types...>>::type get(tuple<_Types...>& _Tuple) { typedef typename tuple_element<_Index, tuple<_Types...>>::_Ttype _Ttype; return (((_Ttype&) _Tuple)._Myfirst); }
tuple<int, char> t1; get<0>(t1) = 959; get<1>(t1) = 'A'; auto v1 = get<0>(t1); auto v2 = get<1>(t1);
代理构造函数已经在C#中用了好长时间,所以将其引入到C++中也很不错。编译器允许一个类型的构造函数(代理构造函数)在其初始化列表中包含另一个构造函数。以前编写代码形式如下:
class Error { public: Error() { Init(0, "Success"); } Error(const char* message) { Init(-1, message); } Error(int errorCode, const char* message) { Init(errorCode, message); } private: void Init(int errorCode, const char* message) { //... } };采用代理构造函数是,可以写成如下形式:
class Error { public: Error() : Error(0, "Success") { } Error(const char* message) : Error(-1, message) { } Error(int errorCode, const char* message) { // ... } };相关阅读- Herb Sutter和Jim Hyslop在十年前(2003年5月)写的一篇有趣的关于代理构造函数的文章。
这是VC++ 2013现在支持的另一项C++ 11特性。目前为止,下面的代码仍然无法通过VC++编译。
template <typename T = int> void Foo(T t = 0) { } // error C4519: default template arguments are only // allowed on a class templateVisual C++ 2013 能够顺利编译这些代码,模板参数推断也能正确进行。
Foo(12L); // Foo<long> Foo(12.1); // Foo<double> Foo('A'); // Foo<char> Foo(); // Foo<int>这项特性的实用性在下面的例子里尤为明显。
template <typename T> class Manager { public: void Process(T t) { } }; template <typename T> class AltManager { public: void Process(T t) { } }; template <typename T, typename M = Manager<T>> void Manage(T t) { M m; m.Process(t); } Manage(25); // Manage<int, Manager<int>> Manage<int, AltManager<int>>(25); // Manage<int, AltManager<int>>并不是所有的参数都需要默认参数。
template <typename B, typename T = int> void Bar(B b = 0, T t = 0) { } Bar(10); // Bar<int, int> Bar(10L); // Bar<long, int> Bar(10L, 20L); // Bar<long, long> Bar(); // will not compile如果带默认参数的函数模板有重载的话,类型无法推断的时候编译器将会给出错误。
template <typename T = int> void Foo(T t = 0) { } template <typename B, typename T = int> void Foo(B b = 0, T t = 0) { } Foo(12L); // will not compile Foo(12.1); // will not compile Foo('A'); // will not compile Foo(); // Foo<int>
使用函数模板的默认模板参数时应当在这里注意。
我仍然记得2004年八月的一天,那个时候我意识到尽管我是一个还不错的C++程序员,我对explicit关键字一无所知,这令我十分局促不安。那之后我写了一篇博客文章
简单说明一下explicit的使用。考虑一下下面的例子。
class Test1 { public: explicit Test1(int) { } }; void Foo() { Test1 t1(20); Test1 t2 = 20; // will not compile }尽管转换构造函数可以达到这一目的,转换运算符因为缺乏标准支持而无法完成类似的任务。坏消息是你无法确保转换构造函数和转换运算符的行为是一致的。考虑一下下面的例子。
class Test1 { public: explicit Test1(int) { } }; class Test2 { int x; public: Test2(int i) : x(i) { } operator Test1() { return Test1(x); } }; void Foo() { Test2 t1 = 20; Test1 t2 = t1; // will compile }上面的代码能通过编译。现在有了C++ 11的新特性,explicit也可以用在转换运算符上了。
class Test2 { int x; public: Test2(int i) : x(i) { } explicit operator Test1() { return Test1(x); } }; void Foo() { Test2 t1 = 20; Test1 t2 = (Test1)t1; // this compiles Test1 t3 = t1; // will not compile }下面的这个例子里隐式应用了bool类型的转换运算符。
class Test3 { public: operator bool() { return true; } }; void Foo() { Test3 t3; if (t3) { } bool b = t3; }这段代码能通过编译。现在试一下在运算符上加上explicit关键字
class Test3 { public: explicit operator bool() { return true; } }; void Foo() { Test3 t3; if (t3) // this compiles! { } bool b = t3; // will not compile }
正如预期,第二个转换无法通过编译,但是第一个通过了。这是因为if里的bool转换被视为显式转换。因此在这里你要小心谨慎,仅仅添加explicit关键字无法防止意外情况下的类型转换,类型可能仍然是不安全的。
评论删除后,数据将无法恢复
评论(42)
不要社么说,microsoft在大中国的ZF采购中绝对占比到80%以上,采购了windows平台,vs就是大行其道的理由,所以以后中国的在vs开发者不会减少,虽然这里是开源社区。大家都看不起ms
http://cpprocks.com/c11-compiler-support-shootout-visual-studio-gcc-clang-intel/
看来得换个环境了
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
这些东西一年前的时候gcc和clang都做好了,现在发这个有毛用?今年3月份gcc已完全支持c++11的所有特性,clang已经在计划支持c++14了。完全过时的东西就不要再发了
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
这些东西一年前的时候gcc和clang都做好了,现在发这个有毛用?今年3月份gcc已完全支持c++11的所有特性,clang已经在计划支持c++14了。完全过时的东西就不要再发了
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
这些东西一年前的时候gcc和clang都做好了,现在发这个有毛用?今年3月份gcc已完全支持c++11的所有特性,clang已经在计划支持c++14了。完全过时的东西就不要再发了
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
引用来自“int03h”的评论
引用来自“有人知是燕归来”的评论
这些东西一年前的时候gcc和clang都做好了,现在发这个有毛用?今年3月份gcc已完全支持c++11的所有特性,clang已经在计划支持c++14了。完全过时的东西就不要再发了