Visual Studio 2017 中的 C++ 一致性改进

工程迁移至VC2017时产生一些新的warning,可参见本文解决!


新语言功能

编译器支持通用 constexpr 和聚合的 NSDMI,现具有 C++14 标准版中的全部新增功能。 请注意,编译器仍缺少 C++11 和 C++98 标准版中的一些功能。 请参阅 Visual C++ 语言合规性中显示编译器当前状态的表。

C++11:

在更多库中支持表达式 SFINAE - Visual C++ 编译器持续改进对表达式 SFINAE 的支持,SFINAE 是模板参数推导和替换所必需的,其中 decltype 和 constexpr 表达式可能显示为模板参数。 有关详细信息,请参阅 Visual Studio 2017 RC 中的表达式 SFINAE 改进之处

C++ 14:

用于聚合的 NSDMI - 聚合是一个数组或类,不具有用户提供的构造函数、专用或受保护的非静态数据成员、基类,也不具有虚拟函数。 从 C++14 开始,聚合可能包含成员初始值设定项。 有关详细信息,请参阅 Member initializers and aggregates(成员初始值设定项和聚合)。

constexpr - 现在允许声明为 constexpr 的表达式包含某些种类的声明:if 和 switch 语句、loop 语句,以及某些对象的突变,这些对象的生命期开始时间处于 constexpr 表达式计算范围内。 此外,不再需要 constexpr 非静态成员函数为隐式 const。 有关详细信息,请参阅 Relaxing constraints on constexpr functions(放松对 constexpr 函数的约束)。

C++17:

Terse static_assert(适用于 /std:c++latest)在 C++17 中,static_assert 的消息参数是可选的。 有关详细信息,请参阅 Extending static_assert, v2(扩展 static_assert, v2)。

[[fallthrough]] 属性(适用于 /std:c++latest)[[fallthrough]] 属性可以在 switch 语句的上下文中用作对预期发生贯穿行为的编译器的提示。 这可防止编译器在这类情况下发出警告。 有关详细信息,请参阅 Wording for [[fallthrough]] attribute([[fallthrough]] 属性的用词)。

通用的基于范围的 for 循环(不需要编译器开关)基于范围的 for 循环不再需要 begin() 和 end() 返回相同类型的对象。 这使得 end() 能够返回类似于 Ranges-V3 方案中定义的 ranges 所使用的那种 sentinel 对象。 有关详细信息,请参阅 Generalizing the Range-Based For Loop(通用化基于范围的 for 循环)和 range-v3 library on GitHub(GitHub 上的 range-v3 库)。

有关 Visual Studio 2015 Update 3 中一致性改进的完整列表,请参阅 Visual C++ What's New 2003 through 2015(Visual C++ 2003 至 2015 中的新增功能)。

Bug 修复

复制列表初始化

Visual Studio 2017 使用并非在 Visual Studio 2015 中捕获的初始值设定项列表正确引发与对象创建相关的编译器错误,并可能导致故障或未定义的运行时行为。 根据 N4594 13.3.1.7p1,在复制列表初始化中,编译器需要考虑用于重载决策的显式构造函数,但是如果实际选择了该重载,则必须引发错误。

以下两个示例在 Visual Studio 2015 中编译,但在 Visual Studio 2017 中不编译。

Copy
C++
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 &'

}

为更正此错误,应使用直接初始化:

Copy
C++
A a1{ 1 };
const A& a2{ 1 };

在 Visual Studio 2015 中,编译器以与常规复制初始化相同的方式错误地处理复制列表初始化;它只考虑将转换构造函数用于重载决策。 在以下示例中,Visual Studio 2015 选择 MyInt(23),但 Visual Studio 2017 正确引发错误。

Copy
C++
// 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
}

此示例与上一个示例类似,但引发了不同的错误。 它在 Visual Studio 2015 中成功,在 Visual Studio 2017 中失败 (C2668)。

Copy
C++
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
}

弃用的的 Typedef

Visual Studio 2017 现在针对在类或结构中声明的已弃用 Typedef 发出正确警告。 下面的示例在 Visual Studio 2015 中编译,且未收到警告,但在 Visual Studio 2017 中则引发 C4996。

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

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

constexpr

当条件性运算的左侧操作数在 constexpr 上下文中无效时,Visual Studio 2017 会正确引发错误。 下列代码在 Visual Studio 2015 中进行编译,但不在 Visual Studio 2017 中进行编译:

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

constexpr bool f(const array<1> &arr)
{
       return arr.size() == 10 || arr.size() == 11; // error starting in Visual Studio 2017
}

要更正错误,可将 array::size() 函数声明为 constexpr 或从 f 中删除 constexpr 限定符。

传递给 variadic 函数的类类型

在 Visual Studio 2017 中,传递给 variadic 函数(如 printf)的类或结构必须完全可复制。 传递此类对象时,编译器只是执行按位复制,不会调用构造函数或析构函数。

Copy
C++
#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
}

若要更正错误,可调用一种成员函数,该函数返回可完全复制的类型,

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

或者执行静态强制转换,以在传递对象之前将其进行转换:

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

对于使用 CStringW 生成和管理的字符串,提供的 ‘operator LPCWSTR()’ 应该用来将 CStringW 对象强制转换为格式字符串所需的 C 指针。

Copy
C++
CStringW str1;
CStringW str2;
str1.Format(… , static_cast<LPCWSTR>(str2));

类构造中的 cv 限定符

在 Visual Studio 2015 中,编译器有时会在通过构造函数调用生成类对象时错误地忽略 cv 限定符。 这可能会导致故障或意外的运行时行为。 以下示例在 Visual Studio 2015 中编译,但在 Visual Studio 2017 中引发了编译器错误:

Copy
C++
struct S 
{
    S(int);
    operator int();
};

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

若要更正此错误,将运算符 int() 声明为 const。

对模板中限定名称的访问检查

早期版本的编译器不对某些模板上下文中的限定名称执行访问检查。 这可能会干扰预期的 SFINAE 行为,在这一行为中,预期由于名称的不可访问性,替换会失败。 这可能会导致在运行时发生故障或意外行为,因为编译器错误地调用了运算符的错误重载。 在 Visual Studio 2017 中,引发了编译器错误。 具体错误可能会有所不同,但通常是“C2672 找不到匹配的重载函数”。 下列代码在 Visual Studio 2015 中进行编译,但在 Visual Studio 2017 中引发错误:

Copy
C++
#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. 
}

缺少的模板参数列表

在 Visual Studio 2015 及更早版本中,当模板出现在模板参数列表中(例如作为默认模板参数或非类型模板参数的一部分)时,编译器不会诊断缺少的模板参数列表。 这可能导致不可预知的行为,包括编译器故障或意外的运行时行为。 下列代码在 Visual Studio 2015 中进行编译,但在 Visual Studio 2017 中引发错误。

Copy
C++
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;  

表达式 SFINAE

为了支持表达式 SFINAE,编译器现在会在声明模板而不是实例化模板时分析 decltype 参数。 因此,如果在 decltype 参数中找到非依赖专用化,则它不会被推迟到实例化时间,而会被立即处理,并且将在此时诊断产生的所有错误。

以下示例显示了在声明时引发的这类编译器错误:

Copy
C++
#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");

在匿名命名空间内声明的类

根据 C++ 标准,在匿名命名空间内部声明的类具有内部链接,因此不能导出。 在 Visual Studio 2015 及更早版本中,此规则不是强制执行的。 在 Visual Studio 2017 中,部分强制执行此规则。 下面的示例在 Visual Studio 2017 中引发错误:“错误 C2201: 'const anonymous namespace'::S1::vftable'': 必须具有外部链接才能导出/导入。”

Copy
C++
namespace
{
    struct __declspec(dllexport) S1 { virtual void f() {} }; //C2201
}

在匿名命名空间内声明的类

根据 C++ 标准,在匿名命名空间内部声明的类具有内部链接,因此不能导出。 在 Visual Studio 2015 及更早版本中,此规则不是强制执行的。 在 Visual Studio 2017 中,部分强制执行此规则。 下面的示例在 Visual Studio 2017 中引发错误:“错误 C2201: 'const anonymous namespace'::S1::vftable'': 必须具有外部链接才能导出/导入。”

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

值类成员的默认初始值设定项 (C++/CLI)

在 Visual Studio 2015 及更早版本中,编译器允许(但会忽略)值类成员的默认成员初始值设定项。 值类的默认初始化始终对成员执行零初始化;不允许使用默认构造函数。 在 Visual Studio 2017 中,默认成员初始值设定项引发编译器错误,如下例所示:

Copy
C++
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)

在 Visual Studio 2015 及更早版本中,编译器在某些情况下将默认属性误识别为默认索引器。 有可能通过使用标识符“default”访问该属性来解决这个问题。 在 C++11 中将默认值引入为关键字后,解决方法本身会出现问题。 因此,在 Visual Studio 2017 中,需要解决方法的 Bug 都已修复,现在将“default”用于访问类的默认属性时,编译器会引发错误。

Copy
C++
//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 中,可以通过属性名称同时访问两个值属性:

Copy
C++
#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value;
       r2->Value;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AsEclipse 支持VS2005、VS2008、VS2010 英文版 AsEclipse 是把Eclispe一些方便的编辑功能移植到Visual Studio上。而且让快捷键尽量与Eclipse默认设置保持一致。不管你是否用过Eclipse,相信都会方便你的代码编写。 主要包括: 1 快捷键执行代码整理:ctrl+shift+f (仅支持c#) 2 一次性迅速生成所有的Getter/Setter属性:alt+shift+s, r (对于数据类尤其方便,仅支持c#) 3 显示类型大纲窗体,选之后转到定义:ctrl+shift+t (如果类型过多,仅显示最近编辑过的类型,支持c#、c++) 4 显示当前编辑文档的成员大纲窗体,选之后转到定义:ctrl+o (支持c#、c++、vb) 5 插入代码段功能:alt+shift+z (调用的VS的插入代码段功能) 6 代码段注释/取消注释功能:ctrl+/ (选的代码如果被注释掉,则执行取消注释,否则执行注释,支持c#、c++、vb、xml) 7 在文档编辑的tab页,增加关闭所有文档菜单: ctrl+shift+f4 8 统计代码行数,在点击主菜单: 工具-AsEclipse-CodeLineCounter,即可。 9 你可以通过AsEclipseConfig.xml文件自定义上面那些功能的快捷键。AsEclipseConfig.xml与AsEclipse.dll在同一个目录。如果你想恢复默认设置,则直接删除AsEclipseConfig.xml即可。 ------------------ 注意: 1.安装完成后,会在主菜单的"Tools--AsEclipse"下看到本插件所有的功能。 2.所有的功能,都能在键盘上轻松完成,不需要使用鼠标。可以大大减少你使用鼠标的次数,无论是打开文件,打开一个类,还是定位到方法、变量等,完全可以用键盘实现了。 3.有些功能是VS本来就有,AsEclipse只是给它设置了一些快捷键. 4.关于显示类型大纲:当解决方案打开时,插件会为解决方案的类型建立索引,文件较多,建立索引速度会比较慢,当类型过多时,会自动切换成仅显示最近编辑文件的所有类型。当用户执行重新生成解决方案命令(Rebuild All)时,会重新建立索引。 5.目前没有测试与其它插件的兼容性。 6.AsEclipseConfig.xml是在你装好本插件后,第一次启动Visual Studio时生成。 ------------------ 安装说明:将插件dll和.AddIn配置文件放入到Visual studio 2008应用目录(默认是在 我的文档\Visual Studio 2008 (或者2005、2010))的Addins目录下。如果没有Addins目录,则自己创建一个。最后,重新启动Visual Studio。如果你是2005则替换目录相应替换成2005即可。 如果你在确认正确安装后,在工具菜单下面找不到AsEclipse的菜单,这个时候需要重新启动VS。 ------------------ 删除方法:删除掉dll和配置文件即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值