简体   繁体   English

C ++代码可以在C ++ 03和C ++ 11中有效但是做不同的事情吗?

[英]Can C++ code be valid in both C++03 and C++11 but do different things?

C ++代码是否可以符合C ++ 03标准和C ++ 11标准,但根据编译的标准,可以做不同的事情吗?

The answer is a definite yes. 答案肯定是肯定的。 On the plus side there is: 从好的方面来说,有:

  • Code that previously implicitly copied objects will now implicitly move them when possible. 以前隐式复制对象的代码现在将隐式移动它们。

On the negative side, several examples are listed in the appendix C of the standard. 从消极方面来看,标准的附录C中列出了几个例子。 Even though there are many more negative ones than positive, each one of them is much less likely to occur. 即使有更多的负面因素而不是正面,每一个都不太可能发生。

String literals 字符串文字

#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"

and

#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal

Type conversions of 0 输入0的转化次数

In C++11, only literals are integer null pointer constants: 在C ++ 11中,只有文字是整数空指针常量:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
    f(0*N); // Calls #2; used to call #1
}

Rounded results after integer division and modulo 整数除法和模数后的舍入结果

In C++03 the compiler was allowed to either round towards 0 or towards negative infinity. 在C ++ 03中,允许编译器向0或向负无穷大舍入。 In C++11 it is mandatory to round towards 0 在C ++ 11中,必须向0舍入

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

Whitespaces between nested template closing braces >> vs > > 嵌套模板关闭括号>> vs >>之间的空格

Inside an specialization or instantiation the >> might instead be interpreted as an right-shift in C++03. 在专门化或实例化中, >>可能会被解释为C ++ 03中的右移。 This is more likely to break existing code though: (from http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ ) 这更有可能破坏现有代码:(来自http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
    // fon<fun<9> >(1) >> 2 in both standards
    unsigned int A = fon< fun< 9 > >(1) >>(2);
    // fon<fun<4> >(2) in C++03
    // Compile time error in C++11
    unsigned int B = fon< fun< 9 >>(1) > >(2);
}

Operator new may now throw other exceptions than std::bad_alloc operator new现在可能抛出除std::bad_alloc之外的其他异常

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
    foo *f = new foo();
} catch (std::bad_alloc &) {
    // c++03 code
} catch (std::exception &) {
    // c++11 code
}

User-declared destructors have an implicit exception specification example from What breaking changes are introduced in C++11? 用户声明的析构函数有一个隐式异常规范示例来自C ++ 11中引入了哪些重大更改?

struct A {
    ~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try { 
    A a; 
} catch(...) { 
    // C++03 will catch the exception
} 

size() of containers is now required to run in O(1) 容器的size()现在需要在O(1)中运行

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failure does not derive directly from std::exception anymore std::ios_base::failure不再直接从std::exception派生

While the direct base-class is new, std::runtime_error is not. 虽然直接基类是新的,但std::runtime_error不是。 Thus: 从而:

try {
    std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
    std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
    std::cerr << "Pre-C++11\n";
}

I point you to this article and the follow-up , which has a nice example of how >> can change meaning from C++03 to C++11 while still compiling in both. 我点你这篇文章随访 ,其中有如何一个很好的例子>>可以改变从C ++ 03意为C ++ 11,而在静止编制。

bool const one = true;
int const two = 2;
int const three = 3;

template<int> struct fun {
    typedef int two;
};

template<class T> struct fon {
    static int const three = ::three;
    static bool const one = ::one;
};

int main(void) {
    fon< fun< 1 >>::three >::two >::one; // valid for both  
}

The key part is the line in main , which is an expression. 关键部分是main的一行,它是一个表达式。

In C++03: 在C ++ 03中:

1 >> ::three = 0
=> fon< fun< 0 >::two >::one;

fun< 0 >::two = int
=> fon< int >::one

fon< int >::one = true
=> true

In C++11 在C ++ 11中

fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one

::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false

Congratulations, two different results for the same expression. 恭喜,同一表达的两个不同结果。 Granted, the C++03 one did come up with a warning form Clang when I tested it. 当然,在我测试它时,C ++ 03确实提出了警告形式Clang。

Yes, there are number of changes that will cause the same code to result in different behavior between C++03 and C++11. 是的,有许多更改会导致相同的代码导致C ++ 03和C ++ 11之间的不同行为。 The sequencing rules differences make for some interesting changes including some previously undefined behavior becoming well defined. 排序规则的差异带来了一些有趣的变化,包括一些先前未定义的行为变得很明确。

1. multiple mutations of the same variable within an initializer list 1.初始化列表中相同变量的多个突变

One very interesting corner case would multiple mutations of the same variable within an initializer list, for example: 一个非常有趣的角点案例是初始化列表中同一变量的多个突变,例如:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

In both C++03 and C++11 this is well defined but the order of evaluation in C++03 is unspecified but in C++11 they are evaluated in the order in which they appear . 在C ++ 03和C ++ 11中,这是明确定义的,但C ++ 03中的评估顺序未指定,但在C ++ 11中,它们按照它们出现的顺序进行评估 So if we compile using clang in C++03 mode it provide the following warning ( see it live ): 因此,如果我们在C ++ 03模式下使用clang进行编译,它会提供以下警告( 请参见实时 ):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]

    int arrInt[2] = { count++, count++ } ;

                           ^        ~~

but does not provide a warning in C++11 ( see it live ). 但是在C ++ 11中没有提供警告( 现场直播 )。

2. New sequencing rules make i = ++ i + 1; 2.新的排序规则使i = ++ i + 1; well defined in C++11 在C ++ 11中很好地定义

The new sequencing rules adopted after C++03 means that: C ++ 03之后采用的新排序规则意味着:

int i = 0 ;
i = ++ i + 1;

is no longer undefined behavior in C++11, this is covered in defect report 637. Sequencing rules and example disagree 在C ++ 11中不再是未定义的行为,这在缺陷报告637中有所涉及。 排序规则和示例不一致

3. New sequencing rules also make ++++i ; 3.新的测序规则也使++++ i; well defined in C++11 在C ++ 11中很好地定义

The new sequencing rules adopted after C++03 means that: C ++ 03之后采用的新排序规则意味着:

int i = 0 ;
++++i ;

is no longer undefined behavior in C++11. 在C ++ 11中不再是未定义的行为。

4. Slightly More Sensible Signed Left-Shifts 4.略微更明智的签名左移

Later drafts of C++11 include N3485 which I link below fixed the undefined behavior of shifting a 1 bit into or past the sign bit . 后来的C ++ 11草案包括N3485 ,我在下面链接修复了将1位移入或超过符号位的未定义行为 This is also covered in defect report 1457 . 这也包含在缺陷报告1457中 Howard Hinnant commented on the significance of this change in the thread on Is left-shifting (<<) a negative integer undefined behavior in C++11? Howard Hinnant评论了线程中这种变化的重要性是左移(<<)C ++ 11中的负整数未定义行为吗? .

5. constexpr functions can be treated as compile time constant expressions in C++11 5. constexpr函数可以在C ++ 11中被视为编译时常量表达式

C++11 introduced constexpr functions which: C ++ 11引入了constexpr函数,其中:

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. constexpr说明符声明可以在编译时评估函数或变量的值。 Such variables and functions can then be used where only compile time constant expressions are allowed. 然后可以在仅允许编译时常量表达式的情况下使用这些变量和函数。

while C++03 does not have the constexpr feature we don't have to explicitly use the constexpr keyword since the standard library provides many functions in C++11 as constexpr . 虽然C ++ 03没有constexpr功能,但我们不必显式使用constexpr关键字,因为标准库在C ++ 11中提供了许多函数作为constexpr For example std::numeric_limits::min . 例如std :: numeric_limits :: min Which can lead to different behavior, for example: 这可能导致不同的行为,例如:

#include <limits>

int main()
{
    int x[std::numeric_limits<unsigned int>::min()+2] ;
}

Using clang in C++03 this will cause x to be a variable length array, which is an extension and will generate the following warning: 在C ++ 03中使用clang这将导致x成为一个可变长度数组,这是一个扩展并将生成以下警告:

warning: variable length arrays are a C99 feature [-Wvla-extension]
    int x[std::numeric_limits<unsigned int>::min()+2] ;
         ^

while in C++11 std::numeric_limits<unsigned int>::min()+2 is a compile time constant expression and does not require the VLA extension. 而在C ++ 11中, std::numeric_limits<unsigned int>::min()+2是一个编译时常量表达式,不需要VLA扩展。

6. In C++11 noexcept exception specifications are implicitly generated for your destructors 6.在C ++ 11中,为析构函数隐式生成noexcept异常规范

Since in C++11 user defined destructor has implicit noexcept(true) specification as explained in noexcept destructors it means that the following program: 因为在C ++ 11中,用户定义的析构函数具有隐式noexcept(true)规范,如noexcept析构函数中所述,它表示以下程序:

#include <iostream>
#include <stdexcept>

struct S
{
  ~S() { throw std::runtime_error(""); } // bad, but acceptable
};

int main()
{
  try { S s; }
  catch (...) {
    std::cerr << "exception occurred";
  } 
 std::cout << "success";
}

In C++11 will call std::terminate but will run successfully in C++03. 在C ++ 11中,将调用std::terminate但将在C ++ 03中成功运行。

7. In C++03, template arguments could not have internal linkage 7.在C ++ 03中,模板参数不能具有内部链接

This is covered nicely in Why std::sort doesn't accept Compare classes declared within a function . 这很好地涵盖了为什么std :: sort不接受在函数中声明的比较类 So the following code should not work in C++03: 所以下面的代码不适用于C ++ 03:

#include <iostream>
#include <vector>
#include <algorithm>

class Comparators
{
public:
    bool operator()(int first, int second)
    {
        return first < second;
    }
};

int main()
{
    class ComparatorsInner : public Comparators{};

    std::vector<int> compares ;
    compares.push_back(20) ;
    compares.push_back(10) ;
    compares.push_back(30) ;

    ComparatorsInner comparatorInner;
    std::sort(compares.begin(), compares.end(), comparatorInner);

    std::vector<int>::iterator it;
    for(it = compares.begin(); it != compares.end(); ++it)
    {
        std::cout << (*it) << std::endl;
    }
}

but currently clang allows this code in C++03 mode with a warning unless you use -pedantic-errors flag, which is kind of icky, see it live . 但是目前clang允许这个代码在C ++ 03模式下带有警告,除非你使用-pedantic-errors标志,这有点icky, 看到它的实时

8. >> is not longer ill-formed when closing multiple templates 关闭多个模板时,>> >>不再格式错误

Using >> to close multiple templates is no longer ill-formed but can lead to code with different results in C++03 and C+11. 使用>>关闭多个模板不再格式错误,但可能导致C ++ 03和C + 11中的代码具有不同的结果。 The example below is taken from Right angle brackets and backwards compatibility : 以下示例取自直角括号和向后兼容性

#include <iostream>
template<int I> struct X {
  static int const c = 2;
};
template<> struct X<0> {
  typedef int c;
};
template<typename T> struct Y {
  static int const c = 3;
};
static int const c = 4;
int main() {
  std::cout << (Y<X<1> >::c >::c>::c) << '\n';
  std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}

and the result in C++03 is: 并且C ++ 03中的结果是:

0
3

and in C++11: 在C ++ 11中:

0
0

9. C++11 changes some of std::vector constructors 9. C ++ 11更改了一些std :: vector构造函数

Slightly modified code from this answer shows that using the following constructor from std::vector : 来自此答案的稍微修改过的代码显示使用std :: vector中的以下构造函数:

std::vector<T> test(1);

produces different results in C++03 and C++11: 在C ++ 03和C ++ 11中产生不同的结果:

#include <iostream>
#include <vector>

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};


int main()
{
    std::vector<T> test(1);
    bool is_cpp11 = !test[0].flag;

    std::cout << is_cpp11 << std::endl ;
}

10. Narrowing conversions in aggregate initializers 10.缩小聚合初始值设定项中的转换

In C++11 a narrowing conversion in aggregate initializers is ill-formed and it looks like gcc allows this in both C++11 and C++03 although it provide a warning by default in C++11: 在C ++ 11中,聚合初始化器中的缩小转换是不正确的,看起来gcc在C ++ 11和C ++ 03中都允许这样,尽管它在C ++ 11中默认提供警告:

int x[] = { 2.0 };

This is covered in the draft C++11 standard section 8.5.4 List-initialization paragraph 3 : 这在C ++ 11标准草案第8.5.4列表初始化3段中有所涉及:

List-initialization of an object or reference of type T is defined as follows: 列表初始化对象或类型T的引用定义如下:

and contains the following bullet ( emphasis mine ): 并包含以下项目( 强调我的 ):

Otherwise, if T is a class type, constructors are considered. 否则,如果T是类类型,则考虑构造函数。 The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). 枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。 If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed 如果转换任何参数需要缩小转换(见下文),则程序格式错误

This and many more instance are covered in the draft C++ standard section annex C.2 C++ and ISO C++ 2003 . C ++标准部分annex C.2 C ++和ISO C ++ 2003中涵盖了这个以及更多实例。 It also includes: 它还包括:

  • New kinds of string literals [...] Specifically, macros named R, u8, u8R, u, uR, U, UR, or LR will not be expanded when adjacent to a string literal but will be interpreted as part of the string literal. 新类型的字符串文字[...]具体来说,名称为R,u8,u8R,u,uR,U,UR或LR的宏在与字符串文字相邻时不会被展开,但会被解释为字符串文字的一部分。 For example 例如

     #define u8 "abc" const char *s = u8"def"; // Previously "abcdef", now "def" 
  • User-defined literal string support [...]Previously, #1 would have consisted of two separate preprocessing tokens and the macro _x would have been expanded. 用户定义的文字字符串支持[...]以前,#1将由两个单独的预处理标记组成,宏_x将被扩展。 In this International Standard, #1 consists of a single preprocessing tokens, so the macro is not expanded. 在本国际标准中,#1由单个预处理令牌组成,因此宏不会扩展。

     #define _x "there" "hello"_x // #1 
  • Specify rounding for results of integer / and % [...] 2003 code that uses integer division rounds the result toward 0 or toward negative infinity, whereas this International Standard always rounds the result toward 0. 为整数/和%[...] 2003代码的结果指定舍入,使用整数除法将结果舍入为0或向负无穷大,而此国际标准始终将结果舍入为0。

  • Complexity of size() member functions now constant [...] Some container implementations that conform to C++ 2003 may not conform to the specified size() requirements in this International Standard. size()成员函数的复杂性现在是常量[...]符合C ++ 2003的某些容器实现可能不符合本国际标准中指定的size()要求。 Adjusting containers such as std::list to the stricter requirements may require incompatible changes. 将诸如std :: list之类的容器调整为更严格的要求可能需要进行不兼容的更改。

  • Change base class of std::ios_base::failure [...] std::ios_base::failure is no longer derived directly from std::exception, but is now derived from std::system_error, which in turn is derived from std::runtime_error. 更改std :: ios_base :: failure [...] std :: ios_base :: failure的基类不再直接从std :: exception派生,而是从std :: system_error派生而来,而std :: system_error又派生自的std :: runtime_error。 Valid C++ 2003 code that assumes that std::ios_base::failure is derived directly from std::exception may execute differently in this International Standard. 假定std :: ios_base :: failure直接从std :: exception派生的有效C ++ 2003代码可能在本国际标准中执行不同。

One potentially dangerous backward-incompatible change is in constructors of sequence containers such as std::vector , specifically in the overload specifying initial size. 一个潜在危险的向后不兼容的变化是序列容器的构造函数,例如std::vector ,特别是在指定初始大小的重载中。 Where in C++03, they copied a default-constructed element, in C++11 they default-construct each one. 在C ++ 03中,他们复制了一个默认构造的元素,在C ++ 11中,他们默认构造每个元素。

Consider this example (using boost::shared_ptr so that it's valid C++03): 考虑这个例子(使用boost::shared_ptr这样它就是有效的C ++ 03):

#include <deque>
#include <iostream>

#include "boost/shared_ptr.hpp"


struct Widget
{
  boost::shared_ptr<int> p;

  Widget() : p(new int(42)) {}
};


int main()
{
  std::deque<Widget> d(10);
  for (size_t i = 0; i < d.size(); ++i)
    std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}

C++03 Live example C ++ 03实例

C++11 Live example C ++ 11 Live示例

The reason is that C++03 specified one overload for both "specify size and prototype element" and "specify size only," like this (allocator arguments omitted for brevity): 原因是C ++ 03为“指定大小和原型元素”和“仅指定大小”指定了一个重载,就像这样(为简洁省略了allocator参数):

container(size_type size, const value_type &prototype = value_type());

This will always copy prototype into the container size times. 这将始终将prototype复制到容器size时间。 When called with just one argument, it will therefore create size copies of a default-constructed element. 当使用一个参数调用时,它将创建默认构造元素的size副本。

In C++11, this constructor signature was removed and replaced with these two overloads: 在C ++ 11中,删除了此构造函数签名并替换为这两个重载:

container(size_type size);

container(size_type size, const value_type &prototype);

The second one works as before, creating size copies of the prototype element. 第二个像以前一样工作,创建prototype元素的size副本。 However, the first one (which now handles calls with only the size argument specified) default-constructs each element individually. 但是,第一个(现在只处理指定了size参数的调用)默认构造每个元素。

My guess for the reason of this change is that the C++03 overload wouldn't be usable with a move-only element type. 我猜测这种变化的原因是C ++ 03重载不适用于只移动元素类型。 But it's a breaking change none the less, and one seldom documented at that. 但它仍然是一个突破性的变化,而且很少有人记录在案。

The result of a failed read from an std::istream has changed. std::istream读取失败的结果已更改。 CppReference summarizes it nicely: CppReference很好地总结了它:

If extraction fails (eg if a letter was entered where a digit is expected), value is left unmodified and failbit is set. 如果提取失败(例如,如果输入了预期数字的字母),则value保持failbit并设置failbit (until C++11) (直到C ++ 11)

If extraction fails, zero is written to value and failbit is set. 如果提取失败,则将零写入value并设置failbit If extraction results in the value too large or too small to fit in value , std::numeric_limits<T>::max() or std::numeric_limits<T>::min() is written and failbit flag is set. 如果提取结果的值太大或太小std::numeric_limits<T>::max()适合value ,则写入std::numeric_limits<T>::max()std::numeric_limits<T>::min()并设置failbit标志。 (since C++11) (自C ++ 11以来)

This is primarily an issue if you are used to the new semantics and then have to write using C++03. 如果您习惯于新语义然后必须使用C ++ 03编写,那么这主要是一个问题。 The following is not particularly good practice but well-defined in C++11: 以下不是特别好的做法,但在C ++ 11中定义良好:

int x, y;
std::cin >> x >> y;
std::cout << x + y;

However, in C++03, the above code uses an uninitialized variable and thus has undefined behaviour. 但是,在C ++ 03中,上面的代码使用了未初始化的变量,因此具有未定义的行为。

This thread What differences, if any, between C++03 and C++0x can be detected at run-time has examples (copied from that thread) to determine language differences, for example by exploiting C++11 reference collapsing: 此线程在运行时可以检测到C ++ 03和C ++ 0x之间的差异(如果有的话)有示例(从该线程复制)以确定语言差异,例如通过利用C ++ 11引用折叠:

template <class T> bool f(T&) {return true; } 
template <class T> bool f(...){return false;} 

bool isCpp11() 
{
    int v = 1;
    return f<int&>(v); 
}

and c++11 allowing local types as template parameters: 和c ++ 11允许本地类型作为模板参数:

template <class T> bool cpp11(T)  {return true;} //T cannot be a local type in C++03
                   bool cpp11(...){return false;}

bool isCpp0x() 
{
   struct local {} var; //variable with local type
   return cpp11(var);
}

Here's another example: 这是另一个例子:

#include <iostream>

template<class T>
struct has {
  typedef char yes;
  typedef yes (&no)[2];    
  template<int> struct foo;    
  template<class U> static yes test(foo<U::bar>*);      
  template<class U> static no  test(...);    
  static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

enum foo { bar };

int main()
{
    std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}

Prints: 打印:

Using c++03: no
Using c++11: yes

See the result on Coliru 查看Coliru的结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM