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

What changes introduced in C++14 can potentially break a program written in C++11?

提问人:Filip Roséen - refp 提问时间:6/1/2014 最后编辑:Jean-François FabreFilip Roséen - refp 更新时间:7/8/2023 访问量:51758

问:

这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或交互。

介绍

使用 C++14(又名。C++1y)标准在接近最终状态时,程序员必须问自己向后兼容性,以及与此相关的问题。


问题

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

如果能够解释前面提到的附录中的这些潜在问题,也许可以借助与附录中提到的内容相关的任何正式文件,那将是有帮助的。

  • 根据标准C++14 中引入的哪些更改可能会破坏用 C++11 编写的程序?
C 11 C++14 语言律师 C++常见问题

评论


答:

233赞 Filip Roséen - refp 6/1/2014 #1

注意:在这篇文章中,我认为“重大变更”是其中之一,或者两者兼而有之;
1. 在编译为 C++14 时会使合法的 C++11 格式不正确的更改;
2. 与 C++11 相比,将更改编译为 C++14 时的运行时行为。


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

标准草案 (n3797) 有一节专门用于此类信息,其中描述了标准的一个修订版与另一个修订版之间的(可能具有破坏性的)差异。

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


C.3.1] 数字分隔符

引入数字分隔符是为了以更易读的方式编写数字文字并编写我们通常(在代码之外)的文字。

int x = 10000000;   // (1)
int y = 10'000'000; // (2), C++14

很容易看出,在上面的代码片段中,(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 }`

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


C.3.2] 大小释放

C++14 引入了声明适合大小释放的全局重载的机会,这在 C++11 中是不可能的。operator delete

但是,该标准还规定,开发人员不能只声明以下两个相关功能之一,它必须声明一个,或者同时声明两个;这在 [new.delete.single]p11 中说明。

void operator delete (void*) noexcept;
void operator delete (void*, std::size_t) noexcept; // sized deallocation

有关潜在问题的更多信息:

重新定义全局未调整大小版本的现有程序也没有 定义大小版本。当实现引入 版本,替换将不完整,并且很可能是 程序将调用实现提供的大小的 DealLocator 使用程序员提供的分配器分配的对象。

注意:引自 n3536 - C++ 大小的释放

(注意:更多感兴趣的内容可以在Lawrence Crowl撰写的标题为n3536 - C++大小的Deallocation的论文中找到 )


C.3.3] member-functions,不再隐式constexprconst

在 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

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


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

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

  constexpr int func (float) const {
    return 2;
  }
};

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

// int const x = 1;   <-- C++11
// int const x = 2;   <-- C++14

C.3.4] 删除std::gets

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

当然,这意味着尝试在 C++14 中编译为 C++11 编写的代码,其中使用了这样的函数,很可能只是无法编译。


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

评论

2赞 Filip Roséen - refp 6/2/2014
@JonathanWakely拥有,也会在 C++14 中屈服;这方面没有变化。std::is_same<decltype(i), std::initializer_list<int>>::valueauto i {1}true
4赞 Constructor 6/2/2014
另一个变化是std::d ecay 添加到 std::common_type 的实现中。所以代码 like 变得无效。std::common_type<int&, int&>::type f(int& x){return x;} /*...*/ int x{}; f(x) = 2;
3赞 Jonathan Wakely 6/2/2014
更改是 DR,因此大多数供应商也会更改他们的 C++11 库(如果他们还没有这样做),您将无法检测到 C++11 和 C++14 在这方面的任何差异。common_type
2赞 Lightness Races in Orbit 6/3/2014
最后一个已经导致了 C++1y 半实现的问题,这些半实现是 C11 和 C++11 标头的组合,后者在前者中调用不再存在: stackoverflow.com/q/17775390/560648std::gets
2赞 Ben Voigt 6/16/2014
@Yakk:不过,这并不是一个“突破性”的变化。该行为会根据其设计用于检测的功能的存在而正确变化。