65.9K
CodeProject 正在变化。 阅读更多。
Home

Visual Studio 2013 中适用于本机开发的新功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (35投票s)

2013 年 8 月 6 日

CPOL

11分钟阅读

viewsIcon

70572

本文列出了 Visual Studio 2013 中 Visual C++ 原生开发的新增或增强功能。

引言

Visual Studio 2013 预览版(内部代号 VS12)和 .NET Framework 4.5.1 已于 6 月发布。尽管发布时间比预期早,但它提供了比 Visual Studio 2012 更多的新功能和改进。在本文中,我将讨论一些针对原生开发的变化。更全面的功能列表可在 MSDN 上找到。可以从 这里 下载 Visual Studio 2013。

C++

现在支持新的 C++11 甚至 C++14 编译器和库功能(其中一些在 Visual Studio 2012 的 2012 年 11 月 CTP 中已提供)。这些功能包括:

  • 编译器支持
    • 函数模板的默认模板参数
    • 委托构造函数
    • 显式转换运算符
    • 初始化列表和统一初始化
    • 原始字符串字面量
    • 可变参数模板
    • rvalue/lvalue 引用转换
  • 库支持
    • C++11 显式转换运算符、初始化列表、作用域 enum 和可变参数模板
    • C++14 功能
      • 透明运算符函数
      • make_unique
      • 非成员函数 cbegin()/cend(), rbegin()/rend(), crbegin()/crend()

在接下来的段落中,我将介绍其中一些功能,但不会全部介绍,也不会详细介绍,因为它们已经被讨论了很多次,而且您可以找到所有这些功能的足够附加参考。

委托构造函数

在 C++11 中,一个类的构造函数可以在其初始化列表中调用同一类的另一个构造函数。在此之前,只能调用基类的构造函数。

#include <string>
#include <sstream>

int to_int(std::string const & s)
{
   std::stringstream sstr(s);
   int val;
   sstr >> val;
   return val;
}

class foo
{
   int value;
public:
   foo(std::string const & s) : foo(to_int(s)) {}
   foo(int a) : value(a) {}
};

如果使用 Visual Studio 2012 编译此代码,将发出以下错误。

error C2614: 'foo' : illegal member initialization: 'foo' is not a base or member

显式转换运算符

在 C++ 中,可以通过将单个参数的构造函数标记为 explicit 关键字来防止它们被用作隐式类型转换运算符。在 C++11 中,相同的关键字也可以用于转换运算符,以防止它们被用于隐式转换。

在以下示例中,结构 foo 具有到 int 的转换运算符,该运算符允许隐式转换。

struct foo
{
   operator int() { return 42; }
};

foo f;
int i = f; // OK -> i becomes 42

当我们添加 explicit 关键字时,隐式转换不再可能。

struct foo
{
   explicit operator int() { return 42; }
};

foo f;
int i = f; // error C2440

赋值现在会触发错误

error C2440: 'initializing' : cannot convert from 'foo' to 'int' No user-defined-conversion 
operator available that can perform this conversion, or the operator cannot be called

并需要显式转换才能工作

int i = static_cast<int>(f); // OK

初始化列表和统一初始化

C++ 曾经允许使用初始化列表初始化数组和结构,如下所示:

int arr [] = { 1, 2, 3, 4, 5 };

然而,对于类来说,这是不可能的。以下代码不起作用:

std::vector<int> v = { 1, 2, 3, 4, 5 };

C++11 引入了 std::initializer_list,这是一个位于同名头文件中的模板类,绑定到初始化列表。此模板类可以用作函数(包括构造函数)的参数。采用 initializer_list 的构造函数优先于其他构造函数。在 Visual Studio 2013 中,所有相关的 STL 类型都已更新,具有接受 initializer_list 的构造函数。这使得上述语句完全合法。初始化列表也可以与任何函数一起使用,而不仅仅是构造函数。

int sum(std::initializer_list<int> list)
{
   int s = 0;
   for (auto const & i : list)
      s += i;

   return s;
}

auto s = sum({1, 1, 2, 3, 5, 8});

统一初始化更进一步,构建在初始化列表的语法之上,并为初始化任何类型的对象(数组、类、POD 等)提供了一种统一的方式。请考虑以下代码:

class foo
{
   int x;
public:
   foo() : x(42) {}
   foo(int v) : x(v) {}
};

int i [] = { 42 };
foo f1(42);
foo f2;

有几种初始化对象的方式:

  • 使用 {} 表示初始化列表
  • 使用 () 表示调用构造函数
  • 不带 () 表示调用默认构造函数

在 C++11 中,可以在所有这些可能的情况下使用初始化列表语法。上述示例使用统一初始化重写后如下所示:

int i [] { 42 };
foo f1 { 42 };
foo f2 {};

原始字符串字面量

一些特殊字符(引号、反斜杠)在使用字符串字面量时需要转义。在 C++11 中,可以声明“原始”字符串字面量,编译器会自动转义这些字符。这些原始字符串字面量必须以 R"( 前缀开头,以 )" 后缀结尾。

auto path = R"(c:\windows)";
auto quotedstring = R"(here are "quotes")";
auto multi = R"(this is the first line
               and here comes the second)";

如果你想让字符串包含 "(" 或 ")"? 那么你可以在 "(") 之间使用不同的分隔符(最多 16 个字符)。例如,你可以使用一个起始标记,如 "*()*"

std::cout << R"*("(sample)")*" << std::endl;

make_unique

std::make_unique 构造一个类型的对象(包括数组)并将其包装在 std::unique_ptr 对象中。

auto p = std::make_unique<int>(42);

透明运算符函数

让我们看一下下面的示例:

int arr [] = {1,2,3,4,5};
std::sort(std::begin(arr), std::end(arr), std::greater<int>());

这里的意图是使用库 std::greater 函数对数组进行排序。但这需要元素类型,这有点太冗长了(此外,还会出现各种其他问题)。

因此,“透明”运算符函数通过消除指定类型的要求来解决此问题。透明意味着它们既是完美转发又是完美返回。

std::sort(std::begin(arr), std::end(arr), std::greater<>());
std::map<int, std::string, std::greater<>> m;

这些函数对象使用 autodecltype 以及非同质签名声明,因此它们可以用于与不同类型进行操作(比较或算术)(比较苹果和梨或除法米和秒)。例如,这是 std::greater 的定义方式:

template <> struct greater<void> {
  template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) > std::forward<U>(u))
       { return std::forward<T>(t) > std::forward<U>(u); }
};

附加阅读材料

C99 库

对 C 开发人员来说是个好消息是,Microsoft 在此版本中支持 C99 库函数。已在现有头文件中添加了缺失的声明和实现,并添加了新的(以前不受支持的)头文件。大多数更改已包含在预览版中,但 RTM 版本还将添加新功能。此 博客文章 详细介绍了已添加的内容及其可用位置。

MFC

到目前为止,MFC 有一些已记录的更改,其中最重要的是弃用多字节字符集 (MBCS) 支持。MFC 的 MBCS 版本不再随 Visual Studio 一起分发,但仍可作为单独下载(可以从 这里 获取)。目标是将来完全删除对 MBCS 的支持,只支持 Unicode。请注意,Visual Studio 中仍然可以选择在项目属性中使用多字节字符集,如果选择此选项(或有使用此字符集的现有项目),则会出现 MSBuild 错误,并指示您单独下载并手动安装。您可以在此处找到有关此更改的更多详细信息:这里

此外,还有一些 bug 修复,其中两个是关于 CDatabase 的问题,我曾写过 第一个 bug第二个 bug 的解决方法。

ATL

ATL 的动态链接库已被移除,现在该库仅以头文件和静态库的形式提供。由于 ATL 代码不再依赖于字符集,并且没有调试/发布配置的特定内容,因此只有一个版本的静态库 (atls.lib)。随着 ATL DLL 的移除,**ATL/MFC 跟踪工具**(以前可在 **工具** 菜单中找到)也被移除了。ATLTRACE 宏也已重新实现,仅依赖于 CRT 调试报告函数(如 _CrtDbgReportW)。

C++ REST SDK

Visual Studio 2013 随 C++ REST SDK 1.0 版本一起部署(自 Visual Studio 2013 预览版以来,已发布新版本 1.1.0)。此 SDK,正式名称为 Casablanca 项目,也可在 Codeplex 上获得。它为基于云的客户端-服务器通信提供了一个现代 C++ API。它是跨平台的,可在 Windows(最低要求为 Vista)和 Linux 上运行。

以下示例查询 Google 以获取特定术语(使用 Google 的 REST API)。响应是一个 json 值,用于解析以显示每个结果(未格式化的)标题和 URL。

#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
#include <cpprest/json.h>

#include <iostream>

using namespace utility;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;

using namespace std;

void search_and_print(wstring const & searchTerm)
{
   http_client client(U("https://ajax.googleapis.ac.cn/ajax/services/search/web"));

   auto query =  uri_builder()
      .append_query(L"q", searchTerm)
      .append_query(L"v", L"1.0")
      .append_query(L"rsz", L"4")
      .to_string();

   client
      .request(methods::GET, query)
      .then([](http_response response) -> pplx::task<json::value>
      {
         if(response.status_code() == status_codes::OK)
         {
            return response.extract_json();
         }

         return pplx::task_from_result(json::value());
      })
      .then([](pplx::task<json::value> previousTask)
      {
         try
         {
            json::value const & v = previousTask.get();

            if(!v.is_null())
            {
               auto response = v[L"responseData"];
               auto results = response[L"results"];
               for(auto const & p : results)
               {
                  auto o = p.second;
                  auto url = o[L"url"];
                  auto title = o[L"titleNoFormatting"];

                  wcout << title.as_string() << endl << url.as_string() << endl << endl;
               }
            }
         }
         catch (http_exception const & e)
         {
            wcout << e.what() << endl;
         }
      })
      .wait();
}

如果使用 L"codeproject" 调用此方法,将显示以下结果:

CodeProject - For those who code
https://codeproject.org.cn/

C# - CodeProject
https://codeproject.org.cn/KB/cs/

The Code Project - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/The_Code_Project

Codeproject.net: The Leading Source Code Site on the Net
http://www.codeproject.net/

额外资源

Vector Calling Convention(向量调用约定)

为 x86/x64 原生目标引入了一种新的调用约定(不适用于混合模式 /clr 目标)。它称为向量调用约定,并使用关键字 __vectorcall 指定。还可以使用编译器 /Gv 开关将模块中的所有函数编译为 __vectorcall(除非另有明确指定)。向量调用约定允许利用向量寄存器来传递向量类型参数(SIMD 数据类型 - __m64, __m128, __m256,以及 HVA/HFA 数据类型)。有关此新调用约定的详细信息,请参阅此 博客文章

调试器

Visualizers (natvis 框架)

Visual Studio 2012 附带了一个名为 natvis 的新框架,用于通过 natvis 文件构建原生类型的可视化。这些是 XML 文件,每次调试器启动时都会由 Visual Studio 加载(因此无需重新启动 Visual Studio 即可识别任何更改)。新框架取代了 autoexp.dat 文件,并提供了更好的诊断、版本控制和多文件支持。有关使用 natvis 文件为 C++ 编写调试器类型可视化工具的文档可以在 此处 找到。

Visual Studio 2013 为该框架带来了一些改进,包括:

  • 多对象视图(为类型定义一个以上视图的能力,并通过适当的格式说明符进行访问)
  • 用于跳过内存地址的说明符
  • 可视化对象上的格式说明符传播(例如,对整数向量使用“x”会传播到其元素,并以十六进制显示它们)
  • 在显示字符串中使用类的最终实现名称
  • 支持循环链表

这些新更改记录在此博客文章 《使用 Visual Studio 2013 编写可维护的原生可视化 (natvis)》 中。

C++ 的 Just My Code

现在可以指示调试器在所有显示调用堆栈的窗口(调用堆栈窗口、线程窗口、并行堆栈窗口)中,将连续的非用户代码帧折叠为标记为 **[External Call]** 的注释帧。此功能以前仅适用于 .NET 项目,现在也为 C++ 启用了。默认情况下启用此功能,并可以通过上下文菜单中的“显示外部代码”命令打开和关闭。也可以在 **工具** > **选项** > **调试** > **常规** 中将其完全关闭。

非用户代码定义在 .natjmc 文件中,这些文件与原生可视化文件位于同一位置。用户代码在函数、文件和模块级别定义。添加新文件或修改现有文件将定义什么是用户代码。

但是,有一个不显示非用户代码的例外:当应用程序在非用户代码中停止时,调用堆栈顶部的外部代码将被显示。

要了解有关此功能的更多信息,请阅读 《VS 2013 中的 C++ Just My Code》

调试异步代码

已为调用堆栈窗口和任务窗口(以前称为并行任务)添加了额外的增强功能,用于调试异步代码。这些功能适用于包括 C++ 在内的各种语言。调试任务过去有点困难。当发生异常时,无法确定它是从哪个执行路径触发的。调用堆栈窗口现在显示在创建任务时存在的类堆栈帧。此帧(或帧)显示在实际调用堆栈下方,标记为 [Async Call]。此外,任务窗口还为任务开始时间和持续时间(已安排的时间)增加了其他字段。但是,任务窗口中的这些功能仅适用于 C++ Store 应用程序。

附加阅读材料

JavaScript/原生互操作调试

对于使用 JavaScript 和 C++ 调试构建的 Windows 应用商店应用,无法从同一 Visual Studio 实例调试两种代码类型。在 Visual Studio 2012 中,必须启动两个实例并将两个不同的调试引擎附加到同一进程。Visual Studio 2013 具有“脚本混合调试”调试器,它提供了完整的原生调试体验,并辅以有限的 JavaScript 调试支持。要进行完整的脚本调试,您仍然需要使用“纯脚本调试”。有关更多信息,请阅读 《Visual Studio 2013 中的 JavaScript/原生互操作调试》

IDE 更改

IDE 和生产力工具方面有各种改进和新功能。我发现其中一些更重要(也更显眼)的已列出。有关其他信息,请参阅此 文章。您也可以阅读有关 Visual Studio 2013 中 C++ IDE 性能改进 的博客文章。

C++ 代码格式化

C++ 代码格式化提供了新功能,例如缩进、花括号和关键字的换行、间距和文本换行。请注意,键入分号 (';')、关闭括号 ('}') 或粘贴代码时自动缩进默认启用。您可以从 **工具** > **选项** > **文本编辑器** > **C/C++** > **格式** 中更改代码格式化设置。

花括号补全

IDE 会自动为 C++ 完成与 {, [, (, '" 对应的闭合字符。

自动补全功能

我们许多人都曾至少一次因为忘记在声明类型后添加分号而遇到大量错误。Visual Studio 2013 会在声明 classstructenumunion 后自动添加分号。此外,它还会自动关闭原始字符串字面量 (R"()") 的括号,并在输入 /* 时添加多行注释的闭合 */

切换头文件/代码文件

用于在头文件和源文件之间切换的新命令可在代码编辑器的上下文菜单中找到,称为“切换头文件/代码文件”。该命令的默认快捷键是 Ctrl + K, Ctrl + O

结论

Visual Studio 2013 提供了新的功能和改进,将有助于原生开发人员:对 C++11 甚至 C++14 编译器和库的新支持、对 C99 库的增强支持、捆绑的 C++ REST SDK、调试器改进和新的 IDE 功能。另一方面,MFC 的变化很少(除了 bug 修复和弃用库的 MBCS 版本),ATL 库的发行方式也有所不同(仅头文件和静态库)。Visual Studio 2013 中 Visual C++ 新增功能的官方列表可在 此处 找到。

历史

  • 2013 年 8 月 6 日:初始版本
© . All rights reserved.