C ++代码可以在C ++ 03和C ++ 11中都有效,但做不同的事情吗?
C ++代码是否可以符合C ++ 03标准和C ++ 11标准,但根据编译的标准做不同的事情?
答案是肯定的。 好的一面是:
不利的一面是,标准附录C列出了几个例子。 即使有更多的消极因素而不是积极因素,它们中的每一个都不太可能发生。
字符串文字
#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"
和
#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal
类型转换为0
在C ++ 11中,只有文字是整型空指针常量:
void f(void *); // #1
void f(...); // #2
template<int N> void g() {
f(0*N); // Calls #2; used to call #1
}
整数除法和模数后的舍入结果
在C ++ 03中,编译器被允许向0或向负无穷大转。 在C ++ 11中,必须向0舍入
int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0
嵌套模板右花括号之间的空格>> vs >>>
在专业化或实例化中, >>
可能会被解释为C ++ 03中的右移。 尽管如此,这更有可能破坏现有的代码:(来自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
可能会抛出其他异常,而不是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
}
用户声明的析构函数有一个隐含的异常规范示例从C ++ 11中引入了什么中断更改?
struct A {
~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try {
A a;
} catch(...) {
// C++03 will catch the exception
}
现在需要在O(1)中运行容器的size()
)
std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03
std::ios_base::failure
不再从std::exception
派生出来
希望它直接从std::exception
派生的代码可能会有不同的表现。
我点你这篇文章和跟进,其中有如何一个很好的例子>>
可以改变从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
}
关键部分是main
的行,这是一个表达式。
在C ++ 03中:
1 >> ::three = 0
=> fon< fun< 0 >::two >::one;
fun< 0 >::two = int
=> fon< int >::one
fon< int >::one = true
=> true
在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
恭喜,相同表达的两个不同结果。 当然,C ++ 03在我测试它时确实出现了一个警告表单Clang。
是的,有许多更改会导致相同的代码导致C ++ 03和C ++ 11之间的不同行为。 排序规则的差异会导致一些有趣的变化,包括一些先前未定义的行为变得明确。
1.初始化程序列表中的相同变量的多个突变
一个非常有趣的角落案例会在初始化器列表中出现多个相同变量的突变,例如:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
在C ++ 03和C ++ 11中,这个定义良好,但C ++ 03中的评估顺序没有指定,但在C ++ 11中,它们按它们出现的顺序进行评估。 因此,如果我们在C ++ 03模式下使用clang
进行编译,它会提供以下警告(请参见实况):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
但并未在C ++ 11中提供警告(请参见实时)。
2.新的排序规则使得i = ++ i + 1; 在C ++ 11中有很好的定义
C ++ 03之后采用的新排序规则意味着:
int i = 0 ;
i = ++ i + 1;
在C ++ 11中不再是未定义的行为,这在缺陷报告637中进行了介绍。排序规则和示例不一致
3.新的测序规则也使++++我; 在C ++ 11中有很好的定义
C ++ 03之后采用的新排序规则意味着:
int i = 0 ;
++++i ;
在C ++ 11中不再是未定义的行为。
4.稍微更明智的签名左转
后来的C ++ 11草案包括我在下面链接的N3485
,修正了将1位移入或移过符号位的未定义行为。 这在缺陷报告1457中也有介绍。Howard Hinnant在线程中评论了这种变化的意义是左移(<<)C ++ 11中的一个负整数未定义行为。
5.在C ++ 11中,constexpr函数可以被视为编译时常量表达式
C ++ 11引入了constexpr函数,其中:
constexpr说明符声明可以在编译时评估函数或变量的值。 然后可以使用这些变量和函数,只允许编译时间常量表达式。
虽然C ++ 03没有constexpr特性,但我们不必显式使用constexpr关键字,因为标准库在C ++ 11中提供了许多函数作为constexpr。 例如std :: numeric_limits :: min。 这可能导致不同的行为,例如:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
在C ++ 03中使用clang
会导致x
成为一个可变长度的数组,这是一个扩展,并且会产生以下警告:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
而在C ++ 11中std::numeric_limits<unsigned int>::min()+2
是一个编译时常量表达式,不需要VLA扩展。
6.在C ++ 11中,除非你的析构函数隐含地生成了异常规范
由于在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";
}
在C ++ 11中会调用std::terminate
但会在C ++ 03中成功运行。
7.在C ++ 03中,模板参数不能有内部链接
为什么std :: sort不接受函数中声明的比较类。 所以下面的代码不应该在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;
}
}
但是目前的clang
允许这个代码在C ++ 03模式下发出警告,除非你使用了-pedantic-errors
标志,这种标志很icky,现场看到它。
8. >>在关闭多个模板时不再不合格
使用>>
关闭多个模板不再不合格,但可导致C ++ 03和C + 11中的代码具有不同的结果。 下面的示例取自右括号和向后兼容性:
#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';
}
而在C ++ 03中的结果是:
0
3
并在C ++ 11中:
0
0
9. C ++ 11改变了一些std :: vector构造函数
从这个答案稍微修改的代码显示,使用std :: vector中的以下构造函数:
std::vector<T> test(1);
在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.缩小聚合初始化器中的转换次数
在C ++ 11中,聚合初始值设定项中的缩小转换不合格,它看起来像gcc
在C ++ 11和C ++ 03中都允许这样做,尽管它在C ++ 11中默认提供了一个警告:
int x[] = { 2.0 };
这包括在C ++ 11标准草案部分8.5.4
列表初始化段落3中:
T类型的对象或引用的列表初始化定义如下:
并包含以下项目符号(重点介绍):
否则,如果T是类类型,则考虑构造函数。 列举适用的构造函数,并通过重载分辨率(13.3,13.3.1.7)选择最好的构造函数。 如果需要缩小转换(见下文)来转换任何参数,则该程序不合格
C ++标准部分annex C.2
C ++和ISO C ++ 2003草案涵盖了这个以及更多的实例。它还包括:
新类型的字符串[...]具体来说,名为R,u8,u8R,u,uR,U,UR或LR的宏在与字符串相邻时不会被扩展,但会被解释为字符串文字的一部分。 例如
#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
用户定义的文字字符串支持[...]以前,#1应该包含两个独立的预处理令牌,并且宏_x将被扩展。 在这个国际标准中,#1由单个预处理标记组成,所以宏不扩展。
#define _x "there"
"hello"_x // #1
指定对整数/和%2003 [...]代码使用整数除法的结果进行四舍五入将结果向0或向负无穷大进行取整,而此国际标准总是将结果向0进行取整。
size()成员函数的复杂性现在是常量[...]一些符合C ++ 2003的容器实现可能不符合本国际标准中规定的size()要求。 将容器(如std :: list)调整为更严格的要求可能需要不兼容的更改。
更改std :: ios_base :: failure std :: ios_base :: failure的基类不再直接从std :: exception中派生出来,而是从std :: system_error派生出来,而std :: system_error派生自的std :: runtime_error。 假定std :: ios_base :: failure直接源自std :: exception的有效C ++ 2003代码在本国际标准中的执行方式可能有所不同。
上一篇: Can C++ code be valid in both C++03 and C++11 but do different things?
下一篇: C++11 reverse range