C++ 14中引入的哪些更改可能会破坏用C++ 11编写的程序?

Fil*_*efp 58 c++ language-lawyer c++11 c++14

介绍

随着C++ 14(又名C++ 1y)标准处于接近最终的状态,程序员必须问自己有关向后兼容性以及与此相关的问题.


这个问题

该问题的答案中,指出该标准有一个附录,专门用于有关修订之间变化的信息.

如果可以解释前面提到的附录中的这些潜在问题,或许在任何与那里提到的相关的正式文件的帮助下,将会有所帮助.

  • 根据标准:C++ 14中引入的哪些更改可能会破坏用C++ 11编写的程序?

Fil*_*efp 229

注意:在这篇文章中,我认为" 突破性变化 "是其中之一或两者;
1. a change that will make legal C++11 ill-formed when compiled as C++14, and;


C++ 11 vs C++ 14,标准说什么?

标准草案(n3797)有一个专门用于此类信息的部分,其中描述了标准的一个修订版与另一个修订版之间的(潜在破坏)差异.

这篇文章使用了该部分[diff.cpp11]作为半精心讨论的基础,该讨论涉及可能影响为C++ 11编写的代码的更改,但编译为C++ 14.


C.3.1]数字分隔符

引入了数字分隔符,以便人们可以以更易读的方式编写数字文字,并以更自然的方式将它们拆分.

int x = 10000000;   // (1)
int y = 10'000'000; // (2), C++14
Run Code Online (Sandbox Code Playgroud)

很容易看出(2)在上面的代码片段中比(1)更容易阅读,而两个初始值设定项具有相同的值.

关于此功能的潜在的问题是在单引号始终表示的的开始/结束字符字面C++ 11,但在C++ 14单引号可以是围绕字符文字,或使用以前所示的方式(2).


示例代码段,在C++ 11C++ 14中都是合法的,但具有不同的行为.

#define M(x, ...) __VA_ARGS__

int a[] = { M(1'2, 3'4, 5) };

// int a[] = { 5 };        <-- C++11
// int a[] = { 3'4, 5 };   <-- C++14
//                              ^-- semantically equivalent to `{ 34, 5 }`
Run Code Online (Sandbox Code Playgroud)

(注意:有关单引号作为数字分隔符的更多信息,请参见n3781.pdf)


C.3.2]大小的释放

C++ 14引入了一个声明operator delete适合大小重新分配的全局重载的机会,这在C++ 11中是不可能的.

但是,标准还要求开发人员不能仅声明下面两个相关函数中的一个,它必须声明none两者 ; 这在[new.delete.single] p11中有说明.

void operator delete (void*) noexcept;
void operator delete (void*, std::size_t) noexcept; // sized deallocation
Run Code Online (Sandbox Code Playgroud)


有关潜在问题的进一步信息:

重新定义全局未分级版本的现有程序也不定义大小的版本.当实现引入大小版本时,替换将是不完整的,并且程序可能会在使用程序员提供的分配器分配的对象上调用实现提供的大小的解除分配器.

注意:引用取自n3536 - C++ Sized Deallocation

(注:更多有趣的文章名为n3536 - C++ Sized Deallocation,由Lawrence Crowl撰写)


C.3.3] constexpr成员函数,不再含蓄const

在C++ 14中,constexpr有许多变化,但是唯一会改变C++ 11C++ 14之间语义的变化是标记为constexpr成员函数常量.

这种改变背后的基本原理是允许constexpr 成员函数改变它们所属的对象,这是由于constexpr放松而允许.

struct A { constexpr int func (); };

// struct A { constexpr int func () const; }; <-- C++11
// struct A { constexpr int func ();       }; <-- C++14
Run Code Online (Sandbox Code Playgroud)


关于此更改的推荐材料,以及为什么它足以引入潜在的代码破坏:


示例代码段,在C++ 11C++ 14中都是合法的,但具有不同的行为

struct Obj {
  constexpr int func (int) {
    return 1;
  }

  constexpr int func (float) const {
    return 2;
  }
};
Run Code Online (Sandbox Code Playgroud)

Obj const a = {}; 
int const x = a.func (123);

// int const x = 1;   <-- C++11
// int const x = 2;   <-- C++14
Run Code Online (Sandbox Code Playgroud)

C.3.4]去除 std::gets

std::gets已被删除,因为它是从标准库认为是危险的.

这样做的含义当然是尝试编译为C++ 11编写的代码,在C++ 14中,使用这样的函数很可能只是无法编译.


(注意:有一些方法可以编写无法编译的代码,并且具有不同的行为,这取决于std::gets标准库中删除)

  • 另一个变化是[将std :: decay`添加到`std :: common_type`的实现中](http://stackoverflow.com/questions/21975812/should-stdcommon-type-use-stddecay).所以代码如`std :: common_type <int&,int&> :: type f(int&x){return x;} /*...*/ int x {}; f(x)= 2;`变为无效. (4认同)
  • `common_type`更改是一个DR,因此大多数供应商也会更改他们的C++ 11库(如果他们还没有这样做)并且您将无法检测到C++ 11和C +之间的任何差异在这方面+14. (3认同)
  • @JonathanWakely`std :: is_same <decltype(i),std :: initializer_list <int >> :: value`有'auto i {1}`,在*C++ 14*中也会产生`true`; 这方面没有变化. (2认同)
  • 最后一个已经导致C++ 1y半实现的问题,这些半实现是C11和C++ 11头文件的组合,后者调用前者中不再存在的`std :: gets`:http:// stackoverflow.com/q/17775390/560648 (2认同)
  • @Yakk:不过,这不是一个"破裂"的变化.行为根据其设计用于检测的特征的存在而正确地改变. (2认同)