Visual Studio 2017 版本 15.0、15.315.5 中 C++ 的一致性改进。C++ conformance improvements in Visual Studio 2017 versions 15.0, 15.3 and 15.5.

编译器支持通用 constexpr 和聚合的 NSDMI,现具有 C++14 标准版中的全部新增功能。With support for generalized constexpr and NSDMI for aggregates, the compiler is now complete for features added in the C++14 Standard. 请注意,编译器仍缺少 C++11 和 C++98 标准版中的一些功能。Note that the compiler still lacks a few features from the C++11 and C++98 Standards. 请参阅 Visual C++ 语言合规性中显示编译器当前状态的表。See Visual C++ Language Conformance for a table that shows the current state of the compiler.

C++11C++11

在更多库中支持表达式 SFINAE - Visual C++ 编译器持续改进对表达式 SFINAE 的支持,SFINAE 是模板参数推导和替换所必需的,其中 decltype 和 constexpr 表达式可能显示为模板参数。Expression SFINAE support in more libraries The Visual C++ compiler continues to improve its support for expression SFINAE, which is required for template argument deduction and substitution where decltype and constexpr expressions may appear as template parameters. 有关详细信息,请参阅 Visual Studio 2017 RC 中的表达式 SFINAE 改进之处For more information, see Expression SFINAE improvements in Visual Studio 2017 RC.

C++ 14C++ 14

用于聚合的 NSDMI - 聚合是一个数组或类,不具有用户提供的构造函数、专用或受保护的非静态数据成员、基类,也不具有虚拟函数。NSDMI for Aggregates An aggregate is an array or a class with no user-provided constructor, no private or protected non-static data members, no base classes, and no virtual functions. 从 C++14 开始,聚合可能包含成员初始值设定项。Beginning in C++14 aggregates may contain member initializers. 有关详细信息,请参阅 Member initializers and aggregates(成员初始值设定项和聚合)。For more information, see Member initializers and aggregates.

constexpr - 现在允许声明为 constexpr 的表达式包含某些种类的声明:if 和 switch 语句、loop 语句,以及某些对象的突变,这些对象的生命期开始时间处于 constexpr 表达式计算范围内。Extended constexpr Expressions declared as constexpr are now allowed to contain certain kinds of declarations, if and switch statements, loop statements, and mutation of objects whose lifetime began within the constexpr expression evaluation. 此外,不再需要 constexpr 非静态成员函数为隐式 const。Also, there is no longer a requirement that a constexpr non-static member function be implicitly const. 有关详细信息,请参阅 Relaxing constraints on constexpr functions(放松对 constexpr 函数的约束)。For more information, see Relaxing constraints on constexpr functions.

C++17C++17

Terse static_assert(适用于 /std:c++17)在 C++17 中,static_assert 的消息参数是可选的。Terse static_assert (available with /std:c++17) In C++17 the message parameter for static_assert is optional. 有关详细信息,请参阅 Extending static_assert, v2(扩展 static_assert, v2)。For more information, see Extending static_assert, v2.

[[fallthrough]] 属性(适用于 /std:c++17)[[fallthrough]] 属性可以在 switch 语句的上下文中用作对预期发生贯穿行为的编译器的提示。[[fallthrough]] attribute (available with /std:c++17) The [[fallthrough]] attribute can be used in the context of switch statements as a hint to the compiler that the fall-through behavior is intended. 这可防止编译器在这类情况下发出警告。This prevents the compiler from issuing warnings in such cases. 有关详细信息,请参阅 Wording for [[fallthrough]] attribute([[fallthrough]] 属性的用词)。For more information, see Wording for [[fallthrough]] attribute.

通用的基于范围的 for 循环(不需要编译器开关)基于范围的 for 循环不再需要 begin() 和 end() 返回相同类型的对象。Generalized range-based for loops (no compiler switch required) Range-based for loops no longer require that begin() and end() return objects of the same type. 这使 end() 能够返回 range-v3 中的范围和完成但尚未发布的范围技术规范使用的 sentinel。This enables end() to return a sentinel as used by ranges in range-v3 and the completed-but-not-quite-published Ranges Technical Specification. 有关详细信息,请参阅 Generalizing the Range-Based For Loop(通用化基于范围的 for 循环)。For more information, see Generalizing the Range-Based For Loop.

Visual Studio 2017 版本 15.3 中的改进Improvements in Visual Studio 2017 version 15.3

constexpr lambda 现在可以在常数表达式中使用 Lambda 表达式。constexpr lambdas Lambda expressions may now be used in constant expressions. 有关详细信息,请参阅 Constexpr LambdaFor more information, see Constexpr Lambda.

函数模板中的 if constexpr 函数模板可以包含 if constexpr 语句以启用编译时分支。if constexpr in function templates A function template may contain if constexpr statements to enable compile-time branching. 有关详细信息,请参阅 if constexprFor more information, see if constexpr.

具有初始化表达式的选择语句 if 语句可以包括在该语句本身内的块范围中引入变量的初始化表达式。Selection statements with initializers An if statement may include an initializer that introduces a variable at block scope within the statement itself. 有关详细信息,请参阅具有初始化表达式的选择语句For more information, see Selection statements with initializer.

[[maybe_unused]] 和 [[nodiscard]] 属性 当不使用实体时,新属性不提示警告,或者如果放弃函数调用的返回值,新属性则创建一个警告。[[maybe_unused]] and [[nodiscard]] attributes New attributes to silence warnings when an entity is not used, or to create a warning if the return value of a function call is discarded. 有关详细信息,请参阅 maybe_unused 属性的用词unused、nodiscard 和 fallthrough 属性的建议For more information, see Wording for maybe_unused attribute and Proposal of unused,nodiscard and fallthrough attributes.

使用属性命名空间而不重复 仅在属性列表中启用单个命名空间标识符的新语法。Using attribute namespaces without repetition New syntax to enable only a single namespace identifier in an attribute list. 有关详细信息,请参阅 C++ 中的属性For more information, see Attributes in C++.

结构化绑定 现在可以在单个声明中存储具有其组件各个名称的值,前提是该值是数组、std::tuple 或 std::pair 或者具有所有公共的非静态数据成员。Structured bindings It is now possible in a single declaration to store a value with individual names for its components, when the value is an array, a std::tuple or std::pair, or has all public non-static data members. 有关详细信息,请参阅结构化绑定For more information, see Structured Bindings.

枚举类值的构造规则 现在有一种从有作用域的枚举的基础类型到该枚举本身的隐式/非收缩转换,前提是它的定义不引入枚举器,并且源使用列表初始化语法。Construction rules for enum class values There is now an implicit/non-narrowing conversion from a scoped enumeration's underlying type to the enumeration itself, when its definition introduces no enumerator and the source uses a list-initialization syntax. 有关详细信息,请参阅枚举类值的构造规则For more information, see Construction Rules for enum class Values .

通过值捕获 *this 现在可以通过值捕获 lambda 表达式中的“*this”对象。Capturing *this by value The "*this" object in a lambda expression may now be captured by value. 这样可以在并行和异步操作中实现调用 lambda 的情况,特别是在较新的计算机体系结构中。This enables scenarios in which the lambda will be invoked in parallel and asynchronous operations, especially on newer machine architectures. 有关详细信息,请参阅通过值执行的 *this 的 Lambda 捕获为 [=,*this]For more information, see Lambda Capture of *this by Value as [=,*this].

删除 bool 的 operator++ bool 类型不再支持 operator++。Removing operator++ for bool operator++ is no longer supported on bool types. 有关详细信息,请参阅删除弃用的 operator++(bool)For more information, see Remove Deprecated operator++(bool).

删除弃用的“register”关键字 以前弃用(并被 Visual C++ 编译器忽略)的 register 关键字现在已从该语言中删除。Removing deprecated "register" keyword The register keyword, previously deprecated (and ignored by the Visual C++ compiler), is now removed from the language. 有关详细信息,请参阅删除弃用的 register 关键字For more information, see Remove Deprecated Use of the register Keyword.

有关 Visual Studio 2015 Update 3 中一致性改进的完整列表,请参阅 Visual C++ What's New 2003 through 2015(Visual C++ 2003 至 2015 中的新增功能)。For the complete list of conformance improvements up through Visual Studio 2015, Update 3, see Visual C++ What's New 2003 through 2015.

Visual Studio 2017 版本 15.5 中的改进Improvements in Visual Studio 2017 version 15.5

标有 [14] 的功能即使是在 /std:c++14 模式中也无条件提供。Features marked with [14] are available unconditionally even in /std:c++14 mode.

extern constexpr 的新编译器开关 在早期版本的 Visual Studio 中,编译器常常提供 constexpr 变量内部链接(即使在变量标记为 extern 时亦然)。New compiler switch for extern constexpr In earlier versions of Visual Studio, the compiler always gave a constexpr variable internal linkage even when the variable was marked extern. 在 Visual Studio 2017 版本 15.5 中,新编译器开关 /Zc:externConstexpr 启用符合标准的正确行为。In Visual Studio 2017 version 15.5, a new compiler switch, /Zc:externConstexpr, enables correct standards-conforming behavior. 有关详细信息,请参阅 extern constexpr 链接For more information, see extern constexpr linkage.

删除动态异常规范P0003R5Removing Dynamic Exception Specifications: P0003R5. C++11 已弃用动态异常规范。Dynamic exception specifications were deprecated in C++11. C++17 中已删除此功能,但是弃用的 throw() 规范(仍然)作为 noexcept(true) 的别名严格保留。the feature is removed from C++17, but the (still) deprecated throw() specification is retained strictly as an alias for noexcept(true). 有关详细信息,请参阅动态异常规范的删除和 noexceptFor more information, see Dynamic exception specification removal and noexcept.

not_fn()P0005R4 代替了 not1 和 not2。not_fn() : P0005R4 is a replacement of not1 and not2.

重新组织 enable_shared_from_thisP0033R1在 C++11 中添加了 enable_shared_from_thisRewording enable_shared_from_this: P0033R1 enable_shared_from_this was added in C++11. C++17 标准更新规范以更好地处理某些特殊情况。The C++17 Standard updates the specification to better handle certain corner cases. [14][14]

拼接映射和集P0083R3Splicing Maps And Sets: P0083R3. 此功能从关联容器(例如 map、set、unordered_map、unordered_set)中提取节点,然后将这些节点修改并插入回同一容器或使用相同节点类型的不同容器中。This feature enables extraction of nodes from associative containers (e.g., map, set, unordered_map, unordered_set) which can then be modified and inserted back into the same container or a different container that uses the same node type. (常见用例是从 std::map 提取节点、更改密钥并重新插入。)(A common use case is to extract a node from a std::map, change the key, and reinsert.)

弃用残留库部分P0174R2Deprecating Vestigial Library Parts: P0174R2. 这些年来,C++ 标准库的多个功能已被更新的功能取代,或者已变得不太有用或存在问题。Several features of the C++ Standard library have been superceded by newer features over the years, or else have been found to be not very useful or to be problematic. C++17 中正式弃用了这些功能。These features are officially deprecated in C++17.

删除 std::function 中的分配器支持P0302R1Removing Allocator Support In std::function: P0302R1. 在 C++17 之前,经典模板 std::function 有多个采用分配器参数的构造函数。Prior to C++17 the class template std::function had several constructors that took an allocator argument. 但是,在此上下文中,分配器的使用出现了问题并且语义不明。However, the use of allocators in this context was problematic, and the semantics were unclear. 所以,删除了这些构造函数。Therefore these contructors were removed.

not_fn() 的修复P0358R1Fixes for not_fn(): P0358R1. std::not_fn 的新措词提供对在调用包装器时传播值类别的支持。New wording for std::not_fn provides support of propagation of value category in case of wrapper invocation.

shared_ptr<T[]>、shared_ptr<T[N]>P0414R2shared_ptr<T[]>, shared_ptr<T[N]>: P0414R2. 将库基础知识中的 shared_ptr 更改合并到 C++17。Merging shared_ptr changes from Library Fundamentals to C++17. [14][14]

修复数组的 shared_ptrP0497R0Fixing shared_ptr for Arrays: P0497R0. 修复数组的 shared_ptr 支持。Fixes to shared_ptr support for arrays. [14][14]

阐明 insert_return_typeP0508R0Clarifying insert_return_type: P0508R0. 有唯一键的关联容器和有唯一键的无序容器均有一个返回 insert_return_type 嵌套类型的 insert 成员函数。The associative containers with unique keys, and the unordered containers with unique keys have a member function insert that returns a nested type insert_return_type. 该返回类型现在定义为在容器 Iterator 和 NodeType 上参数化的类型的专用化。That return type is now defined as a specialization of a type that is parameterized on the Iterator and NodeType of the container.

STL 的内联变量P0607R0Inline Variables For The STL: P0607R0.

弃用的 Annex D 功能 C++ 标准的 Annex D 包含所有已弃用的功能,包括 shared_ptr::unique()<codecvt>namespace std::tr1Annex D features deprecated Annex D of the C++ standard contains all the features that have been deprecated, including shared_ptr::unique(), <codecvt>, and namespace std::tr1. 设置 /std:c++17 编译器开关时,Annex D 中几乎所有的标准库功能都被标记为已弃用。When the /std:c++17 compiler switch is set, almost all the Standard Library features in Annex D are marked as deprecated. 有关详细信息,请参阅 Annex D 中的标准库功能被标记为已弃用For more information, see Standard Library features in Annex D are marked as deprecated.

现在,<experimental/filesystem> 中的 std::tr2::sys 命名空间在 /std:c++14 下默认发出弃用警告,在 /std:c++17 下默认删除。The std::tr2::sys namespace in <experimental/filesystem> now emits a deprecation warning under /std:c++14 by default, and is now removed under /std:c++17 by default.

通过避免非标准扩展(类内显式专用化)改进了 iostreams 中的一致性。Improved conformance in iostreams by avoiding a non-Standard extension (in-class explicit specializations).

标准库现在在内部使用变量模板。The Standard Library now uses variable templates internally.

标准库已随 C++17 编译器的更改更新,包括在类型系统中添加 noexcept 和删除动态异常规范。The Standard Library has been updated in response to C++17 compiler changes, including the addition of noexcept in the type system and the removal of dynamic-exception-specifications.

Visual Studio 版本 15.0、15.315.5 中的 Bug 修复Bug fixes in Visual Studio versions 15.0, 15.3, and 15.5

复制列表初始化Copy-list-initialization

Visual Studio 2017 使用并非在 Visual Studio 2015 中捕获的初始值设定项列表正确引发与对象创建相关的编译器错误,并可能导致故障或未定义的运行时行为。Visual Studio 2017 correctly raises compiler errors related to object creation using initializer lists that were not caught in Visual Studio 2015 and could lead to crashes or undefined runtime behavior. 根据 N4594 13.3.1.7p1,在复制列表初始化中,编译器需要考虑用于重载决策的显式构造函数,但是如果实际选择了该重载,则必须引发错误。As per N4594 13.3.1.7p1, in copy-list-initialization, the compiler is required to consider an explicit constructor for overload resolution, but must raise an error if that overload is actually chosen.

以下两个示例在 Visual Studio 2015 中编译,但在 Visual Studio 2017 中不编译。The following two examples compile in Visual Studio 2015 but not in Visual Studio 2017.

struct A
{
    explicit A(int) {} 
    A(double) {}
};

int main()
{
    A a1 = { 1 }; // error C3445: copy-list-initialization of 'A' cannot use an explicit constructor
    const A& a2 = { 1 }; // error C2440: 'initializing': cannot convert from 'int' to 'const A &'

}

为更正此错误,应使用直接初始化:To correct the error, use direct initialization:

A a1{ 1 };
const A& a2{ 1 };

在 Visual Studio 2015 中,编译器以与常规复制初始化相同的方式错误地处理复制列表初始化;它只考虑将转换构造函数用于重载决策。In Visual Studio 2015, the compiler erroneously treated copy-list-initialization in the same way as regular copy-initialization; it considered only converting constructors for overload resolution. 在以下示例中,Visual Studio 2015 选择 MyInt(23),但 Visual Studio 2017 正确引发错误。In the following example, Visual Studio 2015 chooses MyInt(23) but Visual Studio 2017 correctly raises the error.

// From http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1228
struct MyStore {
       explicit MyStore(int initialCapacity);
};

struct MyInt {
       MyInt(int i);
};

struct Printer {
       void operator()(MyStore const& s);
       void operator()(MyInt const& i);
};

void f() {
       Printer p;
       p({ 23 }); // C3066: there are multiple ways that an object of this type can be called with these arguments
}

此示例与上一个示例类似,但引发了不同的错误。This example is similar to the previous one but raises a different error. 它在 Visual Studio 2015 中成功,在 Visual Studio 2017 中失败 (C2668)。It succeeds in Visual Studio 2015 and fails in Visual Studio 2017 with C2668.

struct A {
    explicit A(int) {}
};

struct B {
    B(int) {}
};

void f(const A&) {}
void f(const B&) {}

int main()
{
    f({ 1 }); // error C2668: 'f': ambiguous call to overloaded function
}

弃用的的 TypedefDeprecated typedefs

Visual Studio 2017 现在针对在类或结构中声明的已弃用 Typedef 发出正确警告。Visual Studio 2017 now issues the correct warning for deprecated typedefs that are declared in a class or struct. 下面的示例在 Visual Studio 2015 中编译,且未收到警告,但在 Visual Studio 2017 中则引发 C4996。The following example compiles without warnings in Visual Studio 2015 but produces C4996 in Visual Studio 2017.

struct A 
{
    // also for __declspec(deprecated) 
    [[deprecated]] typedef int inttype;
};

int main()
{
    A::inttype a = 0; // C4996 'A::inttype': was declared deprecated
}

constexprconstexpr

当条件性运算的左侧操作数在 constexpr 上下文中无效时,Visual Studio 2017 会正确引发错误。Visual Studio 2017 correctly raises an error when the left-hand operand of a conditionally evaluating operation is not valid in a constexpr context. 以下代码在 Visual Studio 2015 中编译,但在 Visual Studio 2017 中不编译(C3615 constexpr 函数 “f” 无法得出常数表达式):The following code compiles in Visual Studio 2015 but not in Visual Studio 2017 (C3615 constexpr function 'f' cannot result in a constant expression):

template<int N>
struct array 
{
       int size() const { return N; }
};

constexpr bool f(const array<1> &arr)
{
       return arr.size() == 10 || arr.size() == 11; // C3615    
}

要更正错误,可将 array::size() 函数声明为 constexpr 或从 f 中删除 constexpr 限定符。To correct the error, either declare the array::size() function as constexpr or remove the constexpr qualifier from f.

传递给 variadic 函数的类类型Class types passed to variadic functions

在 Visual Studio 2017 中,传递给 variadic 函数(如 printf)的类或结构必须完全可复制。In Visual Studio 2017, classes or structs that are passed to a variadic function such as printf must be trivially copyable. 传递此类对象时,编译器只是执行按位复制,不会调用构造函数或析构函数。When passing such objects, the compiler simply makes a bitwise copy and does not call the constructor or destructor.

#include <atomic>
#include <memory>
#include <stdio.h>

int main()
{
    std::atomic<int> i(0);
    printf("%i\n", i); // error C4839: non-standard use of class 'std::atomic<int>'
                        // as an argument to a variadic function
                        // note: the constructor and destructor will not be called; 
                        // a bitwise copy of the class will be passed as the argument
                        // error C2280: 'std::atomic<int>::atomic(const std::atomic<int> &)':
                        // attempting to reference a deleted function

    struct S {
        S(int i) : i(i) {}
        S(const S& other) : i(other.i) {}
        operator int() { return i; }
    private:
        int i;
    } s(0);
    printf("%i\n", s); // warning C4840 : non-portable use of class 'main::S'
                      // as an argument to a variadic function
}

若要更正错误,可调用一种成员函数,该函数返回可完全复制的类型,To correct the error, you can call a member function that returns a trivially copyable type,

    std::atomic<int> i(0);
    printf("%i\n", i.load());

或者执行静态强制转换,以在传递对象之前将其进行转换:or else perform a static cast to convert the object before passing it:

    struct S {/* as before */} s(0);
    printf("%i\n", static_cast<int>(s))

对于使用 CStringW 生成和管理的字符串,提供的 operator LPCWSTR() 应用来将 CStringW 对象强制转换为格式字符串所需的 C 指针。For strings built and managed using CStringW, the provided operator LPCWSTR() should be used to cast a CStringW object to the C pointer expected by the format string.

CStringW str1;
CStringW str2;
str1.Format(L"%s", static_cast<LPCWSTR>(str2));

类构造中的 cv 限定符cv-qualifiers in class construction

在 Visual Studio 2015 中,编译器有时会在通过构造函数调用生成类对象时错误地忽略 cv 限定符。In Visual Studio 2015, the compiler sometimes incorrectly ignores the cv-qualifier when generating a class object via a constructor call. 这可能会导致故障或意外的运行时行为。This can potentially cause a crash or unexpected runtime behavior. 以下示例在 Visual Studio 2015 中编译,但在 Visual Studio 2017 中引发了编译器错误:The following example compiles in Visual Studio 2015 but raises a compiler error in Visual Studio 2017:

struct S 
{
    S(int);
    operator int();
};

int i = (const S)0; // error C2440

若要更正此错误,将运算符 int() 声明为 const。To correct the error, declare operator int() as const.

对模板中限定名称的访问检查Access checking on qualified names in templates

早期版本的编译器不对某些模板上下文中的限定名称执行访问检查。Previous versions of the compiler did not perform access checking on qualified names in some template contexts. 这可能会干扰预期的 SFINAE 行为,在这一行为中,预期由于名称的不可访问性,替换会失败。This can interfere with expected SFINAE behavior where the substitution is expected to fail due to the inaccessibility of a name. 这可能会导致在运行时发生故障或意外行为,因为编译器错误地调用了运算符的错误重载。This could have potentially caused a crash or unexpected behavior at runtime due to the compiler incorrectly calling the wrong overload of the operator. 在 Visual Studio 2017 中,引发了编译器错误。In Visual Studio 2017, a compiler error is raised. 具体错误可能会有所不同,但通常是“C2672 找不到匹配的重载函数”。The specific error might vary but typically it is "C2672 no matching overloaded function found". 下列代码在 Visual Studio 2015 中进行编译,但在 Visual Studio 2017 中引发错误:The following code compiles in Visual Studio 2015 but raises an error in Visual Studio 2017:

#include <type_traits>

template <class T> class S {
       typedef typename T type;
};

template <class T, std::enable_if<std::is_integral<typename S<T>::type>::value, T> * = 0>
bool f(T x);

int main()
{
       f(10); // C2672: No matching overloaded function found. 
}

缺少的模板参数列表Missing template argument lists

在 Visual Studio 2015 及更早版本中,当模板出现在模板参数列表中(例如作为默认模板参数或非类型模板参数的一部分)时,编译器不会诊断缺少的模板参数列表。In Visual Studio 2015 and earlier, the compiler did not diagnose missing template argument lists when the template appeared in a template parameter list (for example as part of a default template argument or a non-type template parameter). 这可能导致不可预知的行为,包括编译器故障或意外的运行时行为。This can result in unpredictable behavior, including compiler crashes or unexpected runtime behavior. 下列代码在 Visual Studio 2015 中进行编译,但在 Visual Studio 2017 中引发错误。The following code compiles in Visual Studio 2015 but produces an error in Visual Studio 2017.

template <class T> class ListNode;
template <class T> using ListNodeMember = ListNode<T> T::*;
template <class T, ListNodeMember M> class ListHead; // C2955: 'ListNodeMember': use of alias 
                                                     // template requires template argument list

// correct:  template <class T, ListNodeMember<T> M> class ListHead;  

表达式 SFINAEExpression-SFINAE

为了支持表达式 SFINAE,编译器现在会在声明模板而不是实例化模板时分析 decltype 参数。To support expression-SFINAE, the compiler now parses decltype arguments when the templates are declared rather than instantiated. 因此,如果在 decltype 参数中找到非依赖专用化,则它不会被推迟到实例化时间,而会被立即处理,并且将在此时诊断产生的所有错误。Consequently, if a non-dependent specialization is found in the decltype argument, it will not be deferred to instantiation-time and will be processed immediately and any resulting errors will be diagnosed at that time.

以下示例显示了在声明时引发的这类编译器错误:The following example shows such a compiler error that is raised at the point of declaration:

#include <utility>
template <class T, class ReturnT, class... ArgsT> class IsCallable
{
public:
       struct BadType {};
       template <class U>
       static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>
       template <class U>
       static BadType Test(...);
       static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};

constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");

在匿名命名空间内声明的类Classes declared in anonymous namespaces

根据 C++ 标准,在匿名命名空间内部声明的类具有内部链接,因此不能导出。According to the C++ standard, a class declared inside an anonymous namespace has internal linkage, and therefore cannot be exported. 在 Visual Studio 2015 及更早版本中,此规则不是强制执行的。In Visual Studio 2015 and earlier, this rule was not enforced. 在 Visual Studio 2017 中,部分强制执行此规则。In Visual Studio 2017 the rule is partially enforced. 下面的示例在 Visual Studio 2017 中引发错误:“错误 C2201: const anonymous namespace::S1::vftable: 必须具有外部链接才能导出/导入。”The following example raises this error in Visual Studio 2017: "error C2201: const anonymous namespace::S1::vftable: must have external linkage in order to be exported/imported."

struct __declspec(dllexport) S1 { virtual void f() {} }; //C2201

值类成员的默认初始值设定项 (C++/CLI)Default initializers for value class members (C++/CLI)

在 Visual Studio 2015 及更早版本中,编译器允许(但会忽略)值类成员的默认成员初始值设定项。In Visual Studio 2015 and earlier, the compiler permitted (but ignored) a default member initializer for a member of a value class. 值类的默认初始化始终对成员执行零初始化;不允许使用默认构造函数。Default initialization of a value class always zero-initializes the members; a default constructor is not permitted. 在 Visual Studio 2017 中,默认成员初始值设定项引发编译器错误,如下例所示:In Visual Studio 2017, default member initializers raise a compiler error, as shown in this example:

value struct V
{
       int i = 0; // error C3446: 'V::i': a default member initializer  
                  // is not allowed for a member of a value class
};

默认索引器 (C++/CLI)Default Indexers (C++/CLI)

在 Visual Studio 2015 及更早版本中,编译器在某些情况下将默认属性误识别为默认索引器。In Visual Studio 2015 and earlier, the compiler in some cases misidentified a default property as a default indexer. 有可能通过使用标识符“default”访问该属性来解决这个问题。It was possible to work around the issue by using the identifier "default" to access the property. 在 C++11 中将默认值引入为关键字后,解决方法本身会出现问题。The workaround itself became problematic after default was introduced as a keyword in C++11. 因此,在 Visual Studio 2017 中,需要解决方法的 Bug 都已修复,现在将“default”用于访问类的默认属性时,编译器会引发错误。Therefore, in Visual Studio 2017 the bugs that required the workaround were fixed, and the compiler now raises an error when "default" is used to access the default property for a class.

//class1.cs

using System.Reflection;
using System.Runtime.InteropServices;

namespace ClassLibrary1
{
    [DefaultMember("Value")]
    public class Class1
    {
        public int Value
        {
            // using attribute on the return type triggers the compiler bug
            [return: MarshalAs(UnmanagedType.I4)]
            get;
        }
    }
    [DefaultMember("Value")]
    public class Class2
    {
        public int Value
        {
            get;
        }
    }
}


// code.cpp
#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value; // error
       r1->default;
       r2->Value;
       r2->default; // error
}

在 Visual Studio 2017 中,可以通过属性名称同时访问两个值属性:In Visual Studio 2017, you can access both Value properties by their name:

#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value;
       r2->Value;
}

Visual Studio 2017 版本 15.3 中的 Bug 修复Bug fixes in Visual Studio 2017 version 15.3

调用的成员模板已遭删除Calls to deleted member templates

在旧版 Visual Studio 中,在某些情况下,编译器可能无法在对已删除的成员模板执行格式错误的调用时发出错误,这可能会导致运行时故障发生。In previous versions of Visual Studio, the compiler in some cases would fail to emit an error for ill-formed calls to a deleted member template which would’ve potentially caused crashes at runtime. 下面的代码现在生成错误 C2280:"int S::f(void)":正在尝试引用已删除的函数。The following code now produces C2280, "'int S::f(void)': attempting to reference a deleted function":

template<typename T> 
struct S { 
   template<typename U> static int f() = delete; 
}; 

void g() 
{ 
   decltype(S<int>::f<int>()) i; // this should fail 
}

若要修复此错误,请将 i 声明为 intTo fix the error, declare i as int.

类型特征的前提条件检查Pre-condition checks for type traits

为了更严格地遵循标准,Visual Studio 2017 版本 15.3 改进了类型特征的前提条件检查。Visual Studio 2017 version 15.3 improves pre-condition checks for type-traits to more strictly follow the standard. 此类检查验证的是类型是否可赋值。One such check is for assignable. 以下代码在 Visual Studio 2017 版本 15.3 中生成错误 C2139:The following code produces C2139 in Visual Studio 2017 version 15.3:

struct S; 
enum E; 

static_assert(!__is_assignable(S, S), "fail"); // C2139 in 15.3
static_assert(__is_convertible_to(E, E), "fail"); // C2139 in 15.3

有关从本机到托管的封送的新编译器警告和运行时检查New compiler warning and runtime checks on native-to-managed marshaling

从托管函数到本机函数的调用需要执行封送。Calling from managed functions to native functions requires marshalling. 虽然 CLR 会执行封送,但并不理解 C++ 语义。The CLR performs the marshaling but it doesn't understand C++ semantics. 如果通过值传递本机对象,CLR 要么会调用对象的复制构造函数,要么会使用 BitBlt,而这可能会导致未定义的运行时行为发生。If you pass a native object by value, CLR will either call the object's copy-constructor or use BitBlt, which may cause undefined behavior at runtime.

现在,如果编译器能够在编译时知晓含有已删除的复制构造函数的本机对象通过值在本机和托管边界之间传递,则会将发出警告。Now the compiler will emit a warning if it can know at compile time that a native object with deleted copy ctor is passed between native and managed boundary by value. 如果编译器在编译时不知晓,则会插入运行时检查,以便在出现格式错误的封送时,程序能够立即调用 std::terminate。For those cases in which the compiler doesn't know at compile time, it will inject a runtime check so that the program will call std::terminate immediately when an ill-formed marshalling occurs. 在 Visual Studio 2017 版本 15.3 中,下面的代码生成错误 C4606:"A": 跨本机和托管边界按值传递参数要求有效的复制构造函数。In Visual Studio 2017 version 15.3, the following code produces C4606 " 'A': passing argument by value across native and managed boundary requires valid copy constructor. 否则会发生未定义的运行时行为。Otherwise the runtime behavior is undefined".

class A 
{ 
public: 
   A() : p_(new int) {} 
   ~A() { delete p_; } 

   A(A const &) = delete; 
   A(A &&rhs) { 
   p_ = rhs.p_; 
} 

private: 
   int *p_; 
}; 

#pragma unmanaged 

void f(A a) 
{ 
} 

#pragma managed 

int main() 
{ 
    f(A()); // This call from managed to native requires marshalling. The CLR doesn't understand C++ and uses BitBlt, which will result in a double-free later. 
}

若要修复此错误,请删除 #pragma managed 指令以将调用方标记为本机,并避免执行封送。To fix the error, remove the #pragma managed directive to mark the caller as native and avoid marshalling.

WinRT 的实验性 API 警告Experimental API warning for WinRT

为了获取反馈而发布的实验性 WinRT API 使用 Windows.Foundation.Metadata.ExperimentalAttribute 进行修饰。WinRT APIs that are released for experimentation and feedback will be decorated with Windows.Foundation.Metadata.ExperimentalAttribute. 在 Visual Studio 2017 版本 15.3 中,编译器会在遇到此特性时生成警告 C4698。In Visual Studio 2017 version 15.3, the compiler will produce warning C4698 when it encounters the attribute. 旧版 Windows SDK 中的一些 API 已使用此特性进行修饰,调用这些 API 会开始触发这一编译器警告。A few APIs in previous versions of the Windows SDK have already been decorated with the attribute, and calls to these APIs will start triggering this compiler warning. 更高版本的 Windows SDK 会从所有已发布的类型中删除此特性。不过,如果使用的是更低版本 SDK,需要对已发布类型的所有调用禁用这些警告。Newer Windows SDKs will have the attribute removed from all shipped types, but if you are using an older SDK, you'll need to suppress these warnings for all calls to shipped types. 下面的代码生成警告 C4698:"Windows::Storage::IApplicationDataStatics2::GetForUserAsync" 仅供评估使用,可能会在今后推出的版本中发生变更或遭到删除。The following code produces warning C4698: "'Windows::Storage::IApplicationDataStatics2::GetForUserAsync' is for evaluation purposes only and is subject to change or removal in future updates":

Windows::Storage::IApplicationDataStatics2::GetForUserAsync() //C4698

若要禁用此警告,请添加 #pragma:To disable the warning, add a #pragma:

#pragma warning(push) 
#pragma warning(disable:4698) 

Windows::Storage::IApplicationDataStatics2::GetForUserAsync() 

#pragma warning(pop)

模板成员函数的外部定义Out-of-line definition of a template member function

Visual Studio 2017 版本 15.3 在遇到未在类中声明的模板成员函数的外部定义时会生成错误。Visual Studio 2017 version 15.3 produces an error when it encounters an out-of-line definition of a template member function that was not declared in the class. 下面的代码现在生成错误 C2039:"f":不是 "S" 的成员。The following code now produces error C2039: 'f': is not a member of 'S':

struct S {}; 

template <typename T> 
void S::f(T t) {} //C2039: 'f': is not a member of 'S'

若要修复此错误,请在类中添加声明:To fix the error, add a declaration to the class:

struct S { 
    template <typename T> 
    void f(T t); 
}; 
template <typename T> 
void S::f(T t) {}

尝试使用“this”指针的地址Attempting to take the address of "this" pointer

在 C++ 中,“this”是指向 X 的类型指针的 prvalue。不能使用“this”的地址,也不能将其绑定到左值引用。In C++ 'this' is an prvalue of type pointer to X. You cannot take the address of 'this' or bind it to an lvalue reference. 在旧版 Visual Studio 中,编译器允许通过执行转换来规避此限制。In previous versions of Visual Studio, the compiler would allow you to circumvent this restriction by performing a cast. 在 Visual Studio 2017 版本 15.3 中,编译器会生成错误 C2664。In Visual Studio 2017 version 15.3, the compiler produces error C2664.

转换成不可访问的基类Conversion to an inaccessible base class

如果尝试将类型转换成不可访问的基类,Visual Studio 2017 版本 15.3 会生成错误。Visual Studio 2017 version 15.3 produces an error when you attempt to convert a type to a base class which is inaccessible. 现在,编译器引发“错误 C2243:‘类型转换’: 存在从‘D *’到‘B *’的转换,但不可访问。”The compiler now raises "error C2243: 'type cast': conversion from 'D *' to 'B *' exists, but is inaccessible". 下面的代码格式错误,可能会导致运行时故障发生。The following code is ill-formed and can potentially cause a crash at runtime. 现在,编译器在遇到如下代码时生成错误 C2243:The compiler now produces C2243 when it encounters code like this:

#include <memory> 

class B { }; 
class D : B { }; // C2243. should be public B { }; 

void f() 
{ 
   std::unique_ptr<B>(new D()); 
}

不允许对成员函数的外部定义使用默认自变量Default arguments are not allowed on out of line definitions of member functions

不允许对模板类中成员函数的外部定义使用默认参数。编译器将在 /permissive 下发出警告,并在 /permissive- 下发出硬错误。Default arguments are not allowed on out-of-line definitions of member functions in template classes The compiler will issue a warning under /permissive, and a hard error under /permissive-.

在以前版本的 Visual Studio 中,以下格式错误的代码可能会导致发生运行时故障。In previous versions of Visual Studio, the following ill-formed code could potentially cause a runtime crash. Visual Studio 2017 版本 15.3 生成警告 C5034:“A::f”: 类模板成员的外部定义不能包含默认参数:Visual Studio 2017 version 15.3 produces warning C5034: 'A::f': an out-of-line definition of a member of a class template cannot have default arguments:


template <typename T> 
struct A { 
    T f(T t, bool b = false); 
}; 

template <typename T> 
T A<T>::f(T t, bool b = false) // C5034
{ 
... 
}

若要修复此错误,请删除“= false”默认自变量。To fix the error, remove the "= false" default argument.

将 offsetof 与复合成员指示符结合使用Use of offsetof with compound member designator

在 Visual Studio 2017 版本 15.3 中,使用 /Wall 选项进行编译时,如果使用 offsetof(T, m)(其中 m 是“复合成员指示符”),将会生成警告。In Visual Studio 2017 version 15.3, using offsetof(T, m) where m is a "compound member designator" will result in a warning when you compile with the /Wall option. 下面的代码格式错误,可能会导致运行时发生故障。The following code is ill-formed and could potentially cause a crash at runtime. Visual Studio 2017 版本 15.3 生成“警告 C4841:使用了非标准扩展: offseto 中的复合成员指示符”:Visual Studio 2017 version 15.3 produces "warning C4841: non-standard extension used: compound member designator in offseto":


struct A { 
int arr[10]; 
}; 

// warning C4841: non-standard extension used: compound member designator in offsetof 
constexpr auto off = offsetof(A, arr[2]);

若要修复此代码,请使用 pragma 禁用此警告,或将此代码更改为不使用 offsetof:To fix the code, either disable the warning with a pragma or change the code to not use offsetof:

#pragma warning(push) 
#pragma warning(disable: 4841) 
constexpr auto off = offsetof(A, arr[2]); 
#pragma warning(pop) 

将 offsetof 与静态数据成员或成员函数结合使用Using offsetof with static data member or member function

在 Visual Studio 2017 版本 15.3 中,使用 offsetof(T, m)(其中 m 表示静态数据成员或成员函数)会导致生成错误。In Visual Studio 2017 version 15.3, using offsetof(T, m) where m refers to a static data member or a member function will result in an error. 下面的代码生成错误 C4597:未定义的行为: offsetof 应用于成员函数 "foo"。同时,还生成错误 C4597:未定义的行为: offsetof 应用于静态数据成员 "bar"。The following code produces "error C4597: undefined behavior: offsetof applied to member function 'foo'" and "error C4597: undefined behavior: offsetof applied to static data member 'bar'":


#include <cstddef> 

struct A { 
int foo() { return 10; } 
static constexpr int bar = 0; 
}; 

constexpr auto off = offsetof(A, foo); 
Constexpr auto off2 = offsetof(A, bar);

此代码格式错误,可能会导致运行时发生故障。This code is ill-formed and could potentially cause a crash at runtime. 若要修复此错误,请将此代码更改为不再调用未定义的行为。To fix the error, change the code to no longer invoke undefined behavior. 这是 C++ 标准不允许使用的不可移植代码。This is non-portable code that is disallowed by the C++ standard.

有关 declspec 属性的新警告New warning on declspec attributes

在 Visual Studio 2017 版本 15.3 中,如果在 extern "C" 链接规范前应用了 __declspec(…),则编译器将不再忽略此特性。In Visual Studio 2017 version 15.3, the compiler no longer ignores attributes if __declspec(…) is applied before extern "C" linkage specification. 以前,编译器会忽略此特性,进而可能会导致运行时影响。Previously, the compiler would ignore the attritbute, which could have runtime implications. 设置 /Wall /WX 选项后,下面的代码生成警告 C4768:已忽略链接规范前的 __declspec 特性。When the /Wall /WX option is set, the following code produces "warning C4768: __declspec attributes before linkage specification are ignored":


__declspec(noinline) extern "C" HRESULT __stdcall //C4768

若要修复此警告,请将 extern "C" 前置:To fix the warning, put extern "C" first:

extern "C" __declspec(noinline) HRESULT __stdcall

此警告默认关闭(在 15.5 中默认打开),只影响使用 /Wall /WX 编译的代码。This warning is off-by-default (on by default in 15.5) and only impacts code compiled with /Wall /WX.

decltype 和调用的析构函数已遭删除decltype and calls to deleted destructors

在旧版 Visual Studio 中,在“decltype”相关表达式的上下文中,编译器无法检测对已删除析构函数的调用。In previous versions of Visual Studio, the compiler did not detect when a call to a deleted destructor occurred in the context of the expression associated with 'decltype'. 在 Visual Studio 2017 版本 15.3 中,下面的代码生成“错误 C2280: ‘A::~A(void)’: 正在尝试引用已删除的函数”:In Visual Studio 2017 version 15.3, the following code produces "error C2280: 'A::~A(void)': attempting to reference a deleted function":

template<typename T> 
struct A 
{ 
   ~A() = delete; 
}; 

template<typename T> 
auto f() -> A<T>; 

template<typename T> 
auto g(T) -> decltype((f<T>())); 

void h() 
{ 
   g(42); 
}

未初始化的 const 变量Uninitialized const variables

Visual Studio 2017 RTW 版本包含一个回归,即如果未初始化“const”变量,C++ 编译器不会发出诊断提醒。Visual Studio 2017 RTW release had a regression in which the C++ compiler would not issue a diagnostic if a 'const' variable was not initialized. Visual Studio 2017 版本 15.3 修复了此回归。This regression has been fixed in Visual Studio 2017 version 15.3. 下面的代码现在生成警告 C4132:“值”:应初始化 const 对象。The following code now produces "warning C4132: 'Value': const object should be initialized":

const int Value; //C4132

若要修复此错误,请向 Value 赋值。To fix the error, assign a value to Value.

空声明Empty declarations

现在,Visual Studio 2017 版本 15.3 不仅会对内置类型发出警告,还会对所有类型的空声明发出警告。Visual Studio 2017 version 15.3 now warns on empty declarations for all types, not just built-in types. 下面的代码现在对所有四种声明生成第 2 级 C4091 警告:The following code now produces a level 2 C4091 warning for all four declarations:

struct A {};
template <typename> struct B {};
enum C { c1, c2, c3 };

int;    // warning C4091 : '' : ignored on left of 'int' when no variable is declared
A;      // warning C4091 : '' : ignored on left of 'main::A' when no variable is declared
B<int>; // warning C4091 : '' : ignored on left of 'B<int>' when no variable is declared
C;      // warning C4091 : '' : ignored on left of 'C' when no variable is declared

若要移除这些警告,只需注释掉或删除空声明即可。To remove the warnings, simply comment-out or remove the empty declarations. 如果未命名的对象会造成副作用(如 RAII),应命名对象。In cases where the un-named object is intended to have a side effect (such as RAII) it should be given a name.

在 /Wv:18 下此警告被排除在外,而在警告等级 W2 下此警告默认启用。The warning is excluded under /Wv:18 and is on by default under warning level W2.

std::is_convertible 用于数组类型std::is_convertible for array types

早期版本的编译器为数组类型提供了不正确的 std::is_convertible 结果。Previous versions of the compiler gave incorrect results for std::is_convertible for array types. 这要求库编写者在使用 std::is_convertible<…> 类型特征时,要特殊处理 Visual C++ 编译器。This required library writers to special-case the Visual C++ compiler when using the std::is_convertible<…> type trait. 在以下示例中,静态断言在早期版本的 Visual Studio 中是通过的,但在 Visual Studio 2017 版本 15.3 中不通过:In the following example, the static asserts pass in earlier versions of Visual Studio but fail in Visual Studio 2017 version 15.3:

#include <type_traits>

using Array = char[1];

static_assert(std::is_convertible<Array, Array>::value);
static_assert(std::is_convertible<const Array, const Array>::value, "");
static_assert(std::is_convertible<Array&, Array>::value, "");
static_assert(std::is_convertible<Array, Array&>::value, "");

std::is_convertible<From, To> 是通过检查虚函数定义是否完整计算而得: std::is_convertible<From, To> is calculated by checking to see if an imaginary function definition is well formed:

   To test() { return std::declval<From>(); }

私有析构函数和 std::is_constructiblePrivate destructors and std::is_constructible

在决定 std::is_constructible 的结果时,早期版本的编译器忽略了析构函数是否是私有的。Previous versions of the compiler ignored whether a destructor was private when deciding the result of std::is_constructible. 现在会考虑这一点。It now considers them. 在以下示例中,静态断言在早期版本的 Visual Studio 中是通过的,但在 Visual Studio 2017 版本 15.3 中不通过:In the following example, the static asserts pass in earlier versions of Visual Studio but fail in Visual Studio 2017 version 15.3:

#include <type_traits>

class PrivateDtor {
   PrivateDtor(int) { }
private:
   ~PrivateDtor() { }
};

// This assertion used to succeed. It now correctly fails.
static_assert(std::is_constructible<PrivateDtor, int>::value);

私有析构函数导致类型不可构造。Private destructors cause a type to be non-constructible. std::is_constructible<T, Args…> 的计算方式类似编写以下声明:std::is_constructible<T, Args…> is calculated as if the following declaration were written:

   T obj(std::declval<Args>()…)

此调用表示一个析构函数调用。This call implies a destructor call.

C2668:重载决策不明确C2668: Ambiguous overload resolution

当发现多个候选项(都使用声明和参数依赖查找)时,早期版本的编译器有时无法检测到多义性。Previous versions of the compiler sometimes failed to detect ambiguity when it found multiple candidates via both using declarations and argument dependent lookup. 这可能导致选择错误的重载并出现异常的运行时行为。This can lead to wrong overload being chosen and unexpected runtime behavior. 在以下示例中,Visual Studio 2017 版本 15.3 正确引发 C2668“f”:对重载函数的调用不确定:In the following example, Visual Studio 2017 version 15.3 correctly raises C2668 'f': ambiguous call to overloaded function:

namespace N {
   template<class T>
   void f(T&, T&);

   template<class T>
   void f();
}

template<class T>
void f(T&, T&);

struct S {};
void f()
{
   using N::f; 

   S s1, s2;
   f(s1, s2); // C2668
}

要修复代码,当打算调用::f() 时,请删除正在使用的 N::f 语句。To fix the code, remove the using N::f statement if you intended to call ::f().

C2660:局部函数声明与参数依赖查找C2660: local function declarations and argument dependent lookup

局部函数声明将函数声明隐藏在封闭作用域中,并禁用参数依赖查找。Local function declarations hide the function declaration in the enclosing scope and disable argument dependent lookup. 但在这种情况中,早期版本的 Visual C++ 编译器会执行参数依赖查找,这有可能导致选择错误的重载,并出现异常的运行时行为。However, previous versions of the Visual C++ compiler performed argument dependent lookup in this case, potentially leading to the wrong overload being chosen and unexpected runtime behavior. 出现此错误,通常是因为局部函数声明的签名不正确。Typically, the error is due to an incorrect signature of the local function declaration. 在下例中,Visual Studio 2017 版本 15.3 正确引发 C2660“f”:函数不具有 2 个参数:In the following example, Visual Studio 2017 version 15.3 correctly raises C2660 'f': function does not take 2 arguments:

struct S {}; 
void f(S, int);

void g()
{
   void f(S); // C2660 'f': function does not take 2 arguments:
   // or void f(S, int);
   S s;
   f(s, 0);
}

要修改此问题,可以更改 f(S) 签名,也可以删除它。To fix the problem, either change the f(S) signature or remove it.

C5038:初始值设定项列表中的初始化顺序C5038: order of initialization in initializer lists

类成员按它们声明的顺序,而非按它们在初始值设定项列表中出现的顺序进行初始化。Class members are initialized in the order they are declared, not the order they appear in initializer lists. 如果初始值设定项列表的顺序不同于声明顺序,早期版本的编译器不会发出警告。Previous versions of the compiler did not warn when the order of the initializer list differed from the order of declaration. 如果列表中为另一成员初始化所依赖的某成员已被初始化,则可能会造成未定义的运行时行为。This could lead to undefined runtime behavior if the intialization of one member depended on another member in the list already being initialized. 在以下示例中,Visual Studio 2017 版本 15.3(具有 /Wall)引发了警告 C5038:数据成员“A::y”将在数据成员“A::x”之后被初始化:In the following example, Visual Studio 2017 version 15.3 (with /Wall) raises warning C5038: data member 'A::y' will be initialized after data member 'A::x':

struct A
{
    A(int a) : y(a), x(y) {} // Initialized in reverse, y reused
    int x;
    int y;
};

要修复此问题,请将初始值设定项列表的顺序设置为与声明顺序相同。To fix the problem arrange the intializer list to have the same order as the declarations. 如果一个或两个初始化表达式同时引用基类成员,则会引发类似警告。A similar warning is raised when one or both initializers refer to base class members.

请注意,警告默认为关闭,它仅会影响通过 /Wall 编译的代码。Note that the warning is off-by-default and only affects code compiled with /Wall.

Visual Studio 2017 版本 15.5 中的 Bug 修复和其他行为更改Bug fixes and other behavior changes in Visual Studio 2017 version 15.5

部分排序更改Partial Ordering Change

现在,编译器正确拒绝以下代码,并提供正确的错误消息:The compiler now correctly rejects the following code and gives the correct error message:


template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(const T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);    // C2668
}
t161.cpp
t161.cpp(16): error C2668: 'f': ambiguous call to overloaded function
t161.cpp(8): note: could be 'int f<int*>(const T &)'
        with
        [
            T=int*
        ]
t161.cpp(2): note: or       'int f<int>(int*)'
t161.cpp(16): note: while trying to match the argument list '(int*)'

上面的示例中的问题是类型中有两种差异(const 和 non-const 以及 pack 和 non-pack)。The problem in the example above is that there are two differences in the types (const vs. non-const and pack vs. non-pack). 要消除编译器错误,请删除其中一种差异。To eliminate the compiler error, remove one of the differences. 这使得编译器能明确地对函数进行排序。This enables the compiler to unambiguously order the functions.

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);
}

异常处理程序Exception handlers

对数组或函数类型的引用的处理程序从不匹配任意异常对象。Handlers of reference to array or function type are never a match for any exception object. 现在,编译器正确遵循此规则并引发第 4 级警告。The compiler now correctly honors this rule and raises a level 4 warning. 使用 /Zc:strictStrings 时,它也不再将 char*wchar_t* 的处理程序与字符串文本相匹配。It also no longer matches a handler of char* or wchar_t* to a string literal when /Zc:strictStrings is used.

int main()
{
    try {
        throw "";
    }
    catch (int (&)[1]) {} // C4843 (This should always be dead code.)
    catch (void (&)()) {} // C4843 (This should always be dead code.)
    catch (char*) {} // This should not be a match under /Zc:strictStrings
}
warning C4843: 'int (&)[1]': An exception handler of reference to array or function type is unreachable, use 'int*' instead
warning C4843: 'void (__cdecl &)(void)': An exception handler of reference to array or function type is unreachable, use 'void (__cdecl*)(void)' instead

以下代码可避免此错误:The following code avoids the error:

catch (int (*)[1]) {}

std::tr1 命名空间已弃用std::tr1 namespace is deprecated

现在,非标准 std::tr1 命名空间在 C++14 和 C++17 这两种模式中均标记为已弃用。The non-Standard std::tr1 namespace is now marked as deprecated in both C++14 and C++17 modes. 在 Visual Studio 2017 版本 15.5 中,以下代码引发错误 C4996:In Visual Studio 2017 version 15.5, the following code raises C4996:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::tr1::function<int (int, int)> f = std::plus<int>(); //C4996
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}
warning C4996: 'std::tr1': warning STL4002: The non-Standard std::tr1 namespace and TR1-only machinery are deprecated and will be REMOVED. You can define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING to acknowledge that you have received this warning.

若要修复此错误,请删除对 tr1 命名空间的引用:To fix the error, remove the reference to the tr1 namespace:

#include <functional>
#include <iostream>
using namespace std;

int main() {
    std::function<int (int, int)> f = std::plus<int>();
    cout << f(3, 5) << std::endl;
    f = std::multiplies<int>();
    cout << f(3, 5) << std::endl;
}

Annex D 中的标准库功能标记为已弃用。Standard Library features in Annex D are marked as deprecated.

设置 /std:c++17 模式编译器开关时,Annex D 中几乎所有的标准库功能都标记为已弃用。When the /std:c++17 mode compiler switch is set, almost all Standard Library features in Annex D are marked as deprecated.

在 Visual Studio 2017 版本 15.5 中,以下代码引发错误 C4996:In Visual Studio 2017 version 15.5, the following code raises C4996:

#include <iterator>

class MyIter : public std::iterator<std::random_access_iterator_tag, int> {
public:
    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");
warning C4996: 'std::iterator<std::random_access_iterator_tag,int,ptrdiff_t,_Ty*,_Ty &>::pointer': warning STL4015: The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. (The <iterator> header is NOT deprecated.) The C++ Standard has never required user-defined iterators to derive from std::iterator. To fix this warning, stop deriving from std::iterator and start providing publicly accessible typedefs named iterator_category, value_type, difference_type, pointer, and reference. Note that value_type is required to be non-const, even for constant iterators. You can define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning.

若要修复此错误,请按警告文本中的说明操作,如以下代码所示:To fix the error, follow the instructions in the warning text, as demonstrated in the following code:

#include <iterator>

class MyIter {
public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef int value_type;
    typedef ptrdiff_t difference_type;
    typedef int* pointer;
    typedef int& reference;

    // ... other members ...
};

#include <type_traits>

static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");

未引用的本地变量Unreferenced local variables

在 Visual Studio 15.5 中,会在较多的情况下发出警告 C4189,如以下代码所示:In Visual Studio 15.5, warning C4189 is emitted in more cases, as shown in the following code:

void f() {
    char s[2] = {0}; // C4189. Either use the variable or remove it.
}
warning C4189: 's': local variable is initialized but not referenced

若要修复此错误,请删除未使用的变量。To fix the error, remove the unused variable.

单行注释Single line comments

在 Visual Studio 2017 版本 15.5 中,警告 C4001 和 C4179 不再由 C 编译器发出。In Visual Studio 2017 version 15.5, warnings C4001 and C4179 are no longer emitted by the C compiler. 以前,仅在 /Za 编译器开关下发出这两个警告。Previously, they were only emitted under the /Za compiler switch. 因为单行注释自 C99 开始已经是 C 标准的一部分,所以不再需要这些警告。The warnings are no longer needed because single line comments have been part of the C standard since C99.

/* C only */
#pragma warning(disable:4001) //C4619
#pragma warning(disable:4179)
// single line comment
//* single line comment */
warning C4619: #pragma warning: there is no warning number '4001'   

如果代码不需要向后兼容,则可以通过删除对 C4001/C4179 的禁止显示来避免警告。If the code does not need to be backwards compatible, you can avoid the warning by removing the C4001/C4179 suppression. 如果代码不需要向后兼容,则仅禁止显示 C4619。If the code does need to be backward compatible, then suppress C4619 only.

/* C only */

#pragma warning(disable:4619)
#pragma warning(disable:4001)
#pragma warning(disable:4179)

// single line comment
/* single line comment */

有外部“C”链接的 __declspec 属性__declspec attributes with extern "C" linkage

在 Visual Studio 的早期版本中,在 extern "C" 链接规范之前应用 __declspec(…) 时,编译器会忽略 __declspec(…) 属性。In earlier versions of Visual Studio, the compiler ignored __declspec(…) attributes when __declspec(…) was applied before the extern "C" linkage specification. 此行为导致生成用户意料之外的代码并且可能产生运行时影响。This behavior caused code to be generated that user didn't intend, with possible runtime implications. Visual Studio 版本 15.3 中添加了此警告,但是默认关闭。The warning was added in Visual Studio version 15.3, but was off by default. Visual Studio 2017 版本 15.5 默认启用此警告。In Visual Studio 2017 version 15.5, the warning is enabled by default.

__declspec(noinline) extern "C" HRESULT __stdcall //C4768
warning C4768: __declspec attributes before linkage specification are ignored

要修复此错误,请将链接规范放在 __declspec 属性之前:To fix the error, place the linkage specification before the __declspec attribute:

extern "C" __declspec(noinline) HRESULT __stdcall

在 Visual Studio 2017 15.3 或更低版本(例如版本 10.0.15063.0,也称为 RS2 SDK)附带的某些 Windows SDK 标头上会有 C4768 这一个新警告。This new warning C4768 will be given on some Windows SDK headers that were shipped with Visual Studio 2017 15.3 or older (for example: version 10.0.15063.0, also known as RS2 SDK). 但是,已修复更高版本 Windows SDK 标头(尤其是 ShlObj.h 和 ShlObj_core.h),所以它们不生成此警告。However, later versions of Windows SDK headers (specifically, ShlObj.h and ShlObj_core.h) have been fixed so that they do not produce this warning. 看见来自 Windows SDK 标头的这一警告时,可以采取以下措施:When you see this warning coming from Windows SDK headers, you can take these actions: 1) 切换到 Visual Studio 2017 15.5 版本附带的最新 Windows SDK。Switch to the latest Windows SDK that came with Visual Studio 2017 15.5 release. 2) 关闭 Windows SDK 标头声明 #include 附近的警告:Turn off the warning around the #include of the Windows SDK header statement:

#pragma warning (push) 
#pragma warning(disable:4768)
#include <shlobj.h>
#pragma warning (pop) 

extern constexpr 链接Extern constexpr linkage

在早期版本的 Visual Studio 中,编译器常常提供 constexpr 变量内部链接,甚至是在变量标记为 extern 的时候。In earlier versions of Visual Studio, the compiler always gave a constexpr variable internal linkage even when the variable was marked extern. 在 Visual Studio 2017 版本 15.5 中,新编译器开关 (/Zc:externConstexpr) 启用符合标准的正确行为。In Visual Studio 2017 version 15.5, a new compiler switch (/Zc:externConstexpr) enables correct standards-conforming behavior. 这最终将成为默认设置。Eventually this will become the default.

extern constexpr int x = 10; 
error LNK2005: "int const x" already defined

如果头文件包含声明 extern constexpr 的变量,需将它标记为 __declspec(selectany)以便正确组合其重复声明:If a header file contains a variable declared extern constexpr, it needs to be marked __declspec(selectany) in order to correctly have its duplicate declarations combined:

extern constexpr __declspec(selectany) int x = 10;

不能在不完整的类类型上使用 typeid。typeid can't be used on incomplete class type.

在早期版本的 Visual Studio 中,编译器不正确地允许以下代码,导致可能不正确的类型信息。In earlier versions of Visual Studio, the compiler incorrectly allowed the following code, resulting in potentially incorrect type information. 在 Visual Studio 2017 版本 15.5 中,编译器正确引发错误:In Visual Studio 2017 version 15.5, the compiler correctly raises an error:

#include <typeinfo>

struct S;

void f() { typeid(S); } //C2027 in 15.5
error C2027: use of undefined type 'S'

std::is_convertible 目标类型std::is_convertible target type

std::is_convertible 要求目标类型为有效返回类型。std::is_convertible requires the target type to be a valid return type. 在早期版本的 Visual Studio 中,编译器不正确地允许抽象类型,这可能导致不正确的重载决策和意外的运行时行为。In earlier versions of Visual Studio, the compiler incorrectly allowed abstract types, which might lead to incorrect overload resolution and unintended runtime behavior. 现在,以下代码正确引发错误 C2338:The following code now correctly raises C2338:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D, B>::value, "fail"); // C2338 in 15.5

若要避免此错误,使用 is_convertible 时应该比较指针类型,因为如果有一个类型是抽象的,那么非指针类型的比较可能会失败:To avoid the error, when using is_convertible you should compare pointer types because a non-pointer-type comparison might fail if one type is abstract:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D *, B *>::value, "fail");

动态异常规范的删除和 noexceptDynamic exception specification removal and noexcept

在 C++17 中,throw()noexcept 的别名,删除了 throw(<type list>)throw(…),并且某些类型可能包含 noexceptIn C++17, throw() is an alias for noexcept, throw(<type list>) and throw(…) are removed, and certain types may include noexcept. 这可能导致符合 C++14 或更低版本的代码的源兼容性问题。This can cause source compatibility issues with code that conforms to C++14 or earlier. 通常,使用 C++17 模式时,可以使用“/Zc:noexceptTypes-”开关还原为 noexcept 的 C++14 版本。The /Zc:noexceptTypes- switch can be used to revert to the C++14 version of noexcept while using C++17 mode in general. 这样可以将源代码更新为符合 C++17,而无需在同时重写所有 throw() 代码。This enables you to update your source code to conform to C++17 without having to rewrite all your throw() code at the same time.

现在,编译器还诊断 C++17 模式声明中或者带有新警告 C5043 的“/permissive-”所具有的更多不匹配的异常规范。The compiler also now diagnoses more mismatched exception specifications in declarations in C++17 mode or with /permissive- with the new warning C5043.

应用 /std:c++17 开关时,以下代码在 Visual Studio 2017 版本 15.5 中生成 C5043 和 C5040:The following code generates C5043 and C5040 in Visual Studio 2017 version 15.5 when the /std:c++17 switch is applied:

void f() throw(); // equivalent to void f() noexcept;
void f() {} // warning C5043
void g() throw(); // warning C5040

struct A {
    virtual void f() throw();
};

struct B : A {
    virtual void f() { } // error C2694
};

要在使用“/std:c++17”的同时删除错误,请将“/Zc:noexceptTypes-”开关添加到命令行,或者更新代码以使用 noexcept,如以下示例中所示:To remove the errors while still using /std:c++17, either add the /Zc:noexceptTypes- switch to the command line, or else update your code to use noexcept, as shown in the following example:

void f() noexcept;
void f() noexcept { }
void g() noexcept(false);

struct A {
    virtual void f() noexcept;
};

struct B : A {
    virtual void f() noexcept { }
};

内联变量Inline variables

现在,静态 constexpr 数据成员是隐式内联的,这意味着它们在类中的声明现在是它们的定义。Static constexpr data members are now implicitly inline, which means that their declaration within a class is now their definition. 使用静态 constexpr 数据成员的外部定义是冗余的,现已弃用。Using an out-of-line definition for a static constexpr data member is redundant, and now deprecated. 在 Visual Studio 2017 版本 15.5 中应用 /std:c++17 开关时,以下代码现在生成警告 C5041:“大小”: 不需要对 constexpr 静态数据成员进行外部定义,且在 C + + 17 中弃用了该类定义:In Visual Studio 2017 version 15.5 when the /std:c++17 switch is applied, the following code now produces warning C5041 'size': out-of-line definition for constexpr static data member is not needed and is deprecated in C++17:

struct X {
    static constexpr int size = 3;
};
const int X::size; // C5041

现在默认打开 extern "C" __declspec(...) 警告 C4768extern "C" __declspec(...) warning C4768 now on by default

Visual Studio 2017 版本 15.3 中添加了此警告,但是默认关闭。The warning was added in Visual Studio 2017 version 15.3 but was off by default. 在 Visual Studio 2017 版本 15.5 中,它默认打开。In Visual Studio 2017 version 15.5 it is on by default. 有关详细信息,请参阅有关 declspec 属性的新警告See New warning on declspec attributes for more information.

默认函数和 __declspec(nothrow)Defaulted functions and __declspec(nothrow)

以前,当相应基/成员函数允许异常时,编译器允许使用 __declspec(nothrow) 声明默认函数。The compiler previously allowed defaulted functions to be declared with __declspec(nothrow) when the corresponding base/member functions permitted exceptions. 此行为与 C++ 标准冲突,可能导致在运行时发生未定义的行为。This behavior is contrary to the C++ Standard and can cause undefined behavior at runtime. 如果有异常规范不匹配,标准要求此类函数定义为已删除。The standard requires such functions to be defined as deleted if there is an exception specification mismatch. 在 /std:c++17 下,以下代码引发错误 C2280:尝试引用已删除的函数因为显式异常规范与隐式声明的异常规范不兼容,所以已隐式删除函数:Under /std:c++17, the following code raises C2280 attempting to reference a deleted function. Function was implicitly deleted because the explicit exception specification is incompatible with that of the implicit declaration.:

struct A {
    A& operator=(const A& other) { // No exception specification; this function may throw.
        ...
    }
};

struct B : public A {
    __declspec(nothrow) B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1; // error C2280
}

要更正此代码,请从默认函数删除 __declspec(nothrow),或者删除 = default 并提供函数定义以及任何所需异常处理:To correct this code, either remove __declspec(nothrow) from the defaulted function, or remove = default and provide a definition for the function along with any required exception handling:

struct A {
    A& operator=(const A& other) {
        ...
    }
};

struct B : public A {
    B& operator=(const B& other) = default;
};

int main()
{
    B b1, b2;
    b2 = b1;
}

noexcept 和部分专用化noexcept and partial specializations

由于类型系统中有 noexcept,由于缺少 noexcept 函数指针的部分专用化,所以匹配特定“可调用”类型的部分专业化可能无法编译或选择主模板。With noexcept in the type system, partial specializations for matching particular "callable" types may fail to compile or choose the primary template due to a missing partial specialization for pointers-to-noexcept-functions.

在这种情况下,可能需要添加额外的部分专用化以处理 noexcept 函数指针和成员函数的 noexcept 指针。In such cases, you may need to add additional partial specializations to handle the noexcept function pointers and noexcept pointers to member functions. 这些重载仅在 /std:c++17 模式中合规。These overloads are only legal in /std:c++17 mode. 如果必须维持与 C++14 的向后兼容性,并且正在编写他人将使用的代码,那么应该保证这些新重载位于 #ifdef 指令内。If backwards-compatibility with C++14 must be maintained, and you are writing code that others will consume, then you should guard these new overloads inside #ifdef directives. 如果在自包含模块中工作,那么可以仅使用“/Zc:noexceptTypes-”开关进行编译,而不是使用 #ifdef 临界子句。If you are working in a self-contained module, then instead of using #ifdef guards you can just compile with the /Zc:noexceptTypes- switch.

以下代码在 /std:c++14 下编译;但不能在 /std:c++17 下编译,错误为 C2027(使用了未定义的类型 "A"):The following code compiles under /std:c++14 but fails under /std:c++17 with error C2027:use of undefined type 'A':

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // C2027
}

以下代码在 /std:c++17 下成功,因为编译器选择新的部分专用化 A<void (*)() noexcept>The following code succeeds under /std:c++17 because the compiler chooses the new partial specialization A<void (*)() noexcept>:

template <typename T> struct A;

template <>
struct A<void(*)()>
{
    static const bool value = true;
};

template <>
struct A<void(*)() noexcept>
{
    static const bool value = true;
};

template <typename T>
bool g(T t)
{
    return A<T>::value;
}

void f() noexcept {}

int main()
{
    return g(&f) ? 0 : 1; // OK
}

请参阅See Also

Visual C/C++ 语言一致性Visual C++ Language Conformance