web-dev-qa-db-ja.com

C ++ 03とC ++ 11の両方でC ++コードを有効にすることはできますか?

C++コードが C++ 標準と C++ 11 標準の両方に準拠することは可能ですが、どの標準に基づいているかによって異なることを行います編集済み?

295
Erik Sjölund

答えは確かにイエスです。プラス側にあります:

  • 以前に暗黙的にオブジェクトをコピーしたコードは、可能であればそれらを暗黙的に移動します。

マイナス面として、標準の付録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

の型変換

C++ 11では、リテラルのみが整数NULLポインター定数です。

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の右シフトとして解釈される場合があります。ただし、これは既存のコードを破壊する可能性が高くなります:(from 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);
}

演算子newstd::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
} 

size()コンテナのO(1)での実行が必要になりました

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

std::ios_base::failurestd::exceptionから直接派生しなくなりました

直接の基本クラスは新しいものですが、std::runtime_errorは新しいものではありません。副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:

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";
}
279
example

この記事フォローアップ を指します。これは、>>が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

おめでとう、同じ式に対する2つの異なる結果。確かに、C++ 03をテストしたときにClangの警告フォームが出てきました。

55
chris

はい、C++ 03とC++ 11の間で同じコードが異なる動作を引き起こす多くの変更があります。シーケンスルールの違いにより、以前は定義されていなかった動作が適切に定義されるなど、いくつかの興味深い変更が行われます。

1.初期化子リスト内の同じ変数の複数の突然変異

非常に興味深いコーナーケースの1つは、初期化リスト内の同じ変数の複数の突然変異です。たとえば、

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

    return 0 ;
}

C++ 03とC++ 11の両方で、これは明確に定義されていますが、C++ 03の 評価の順序は未指定 ですが、 C++ 11ではそれらは出現する順序で評価されます 。したがって、C++ 03モードでclangを使用してコンパイルすると、次の警告が表示されます(see it live):

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

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

                           ^        ~~

c ++ 11では警告を提供しません(live it)。

2.新しい順序付け規則により、i = ++ i + 1になります。 C++ 11で明確に定義されている

C++ 03の後に採用された新しいシーケンス規則は、次のことを意味します。

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

c ++ 11で未定義の動作ではなくなりました。これは 障害レポート637でカバーされています。シーケンスルールと例の不一致

3.新しい順序付け規則により、++++ iも作成されます。 C++ 11で明確に定義されている

C++ 03の後に採用された新しいシーケンス規則は、次のことを意味します。

int i = 0 ;
++++i ;

c ++ 11では未定義の動作ではなくなりました。

4.わずかに賢明な符号付き左シフト

C++ 11の後のドラフトには、以下にリンクするN3485が含まれます。 1ビットを符号ビット に、またはそれ以降にシフトする未定義の動作を修正しました。これは、 障害レポート1457 でも説明されています。 Howard Hinnantは、 のスレッドにおけるこの変更の重要性についてコメントしました。左シフト(<<)はC++ 11での負の整数の未定義動作ですか?

5. constexpr関数は、C++ 11でコンパイル時定数式として扱うことができます。

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 destructors で説明されているように、ユーザー定義のデストラクタには暗黙のnoexcept(true)仕様があるため、次のプログラムを意味します。

#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はfunction 内で宣言されたCompareクラスを受け入れないのですか。したがって、次のコードは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は、-pedantic-errorsフラグを使用しない限り、C++ 03モードでこのコードを警告付きで許可します。これは、ちょっと厄介な live .

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コンストラクターの一部を変更します

this answer のわずかに変更されたコードは、 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.4List-initializationパラグラフ3で説明されています。

タイプTのオブジェクトまたは参照のリスト初期化は、次のように定義されます。

そして、次の箇条書きが含まれています(emphasis mine):

それ以外の場合、Tがクラス型の場合、コンストラクターが考慮されます。該当するコンストラクターが列挙され、最適なコンストラクターがオーバーロード解決(13.3、13.3.1.7)によって選択されます。 引数のいずれかを変換するために縮小変換(以下を参照)が必要な場合、プログラムは不正な形式です

このインスタンスおよびその他の多くのインスタンスは、 ドラフトC++標準 セクションannex C.2C++およびISO C++ 2003で説明されています。以下も含まれます。

  • 新しい種類の文字列リテラル[...]具体的には、R、u8、u8R、u、uR、U、UR、またはLRという名前のマクロは、文字列リテラルに隣接している場合は展開されませんが、文字列リテラルの一部として解釈されます。例えば

    #define u8 "abc"
    const char *s = u8"def"; // Previously "abcdef", now "def"
    
  • ユーザー定義のリテラル文字列のサポート[...]以前は、#1は2つの別個の前処理トークンで構成され、マクロ_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 :: runtime_error。 std :: ios_base :: failureがstd :: exceptionから直接派生していると想定する有効なC++ 2003コードは、この国際標準では異なる方法で実行される場合があります。

37
Shafik Yaghmour

潜在的に危険な後方互換性のない変更の1つは、std::vectorなどのシーケンスコンテナーのコンストラクター、特に初期サイズを指定するオーバーロードです。 C++ 03ではデフォルトで構築された要素をコピーし、C++ 11では各要素をデフォルトで構築しました。

この例を検討してください(有効なC++ 03になるようにboost::shared_ptrを使用):

#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ライブの例

C++ 11ライブの例

その理由は、C++ 03が次のように「サイズとプロトタイプ要素を指定」と「サイズのみを指定」の両方に1つのオーバーロードを指定したためです(簡潔にするためにアロケーター引数は省略)。

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

これは、常にprototypeをコンテナにsize回コピーします。したがって、引数を1つだけ指定して呼び出すと、デフォルトで構築された要素のsizeコピーが作成されます。

C++ 11では、このコンストラクターシグネチャは削除され、次の2つのオーバーロードに置き換えられました。

container(size_type size);

container(size_type size, const value_type &prototype);

2番目は以前と同様に機能し、size要素のprototypeコピーを作成します。ただし、最初の引数(サイズ引数のみを指定して呼び出しを処理するようになった)は、各要素を個別にデフォルトで構築します。

この変更の理由は、C++ 03のオーバーロードは移動のみの要素タイプでは使用できないためだと思います。しかし、それでも壊滅的な変化であり、そのことについてはほとんど文書化されていません。

35
Angew

std::istreamからの読み取りの失敗の結果が変更されました。 CppReference はそれをうまく要約しています:

抽出に失敗した場合(たとえば、数字が必要な場所に文字が入力された場合)、valueは変更されずに残り、failbitが設定されます。 (C++ 11まで)

抽出に失敗すると、valueにゼロが書き込まれ、failbitが設定されます。抽出の結果、大きすぎて小さすぎてvalueに収まらない場合、std::numeric_limits<T>::max()またはstd::numeric_limits<T>::min()が書き込まれ、failbitフラグが設定されます。 (C++ 11以降)

これは主に、新しいセマンティクスに慣れており、C++ 03を使用して記述する必要がある場合の問題です。以下は特に良いプラクティスではありませんが、C++ 11で明確に定義されています。

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

ただし、C++ 03では、上記のコードは初期化されていない変数を使用しているため、動作が未定義です。

19
Anton Golov

このスレッド 実行時に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); 
}

およびテンプレートパラメータとしてローカルタイプを許可する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);
}
15
uwedolinsky

別の例を次に示します。

#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;
}

プリント:

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

Coliruの結果を参照

7
StackedCrooked