C ++コードはC ++ 03とC ++ 11の両方で有効ですが、異なることはできますか?


298

C ++コードをC ++ 03標準とC ++ 11標準の両方に準拠させることはできますが、コンパイルする標準に応じて異なる処理を実行できますか?


26
私はautoこのような状況になる可能性があると確信しています
OMGtechy 14

8
はい。1つの例は>>、テンプレートで使用される場合です。あなたはそれが両方の標準のためにコンパイルできる状況を思いつくことができます。変更を見つけるのが簡単だと私が確信しているもう1つは、初期化です。
クリス

5
>>の状況に関する素晴らしい記事を以下に示します。gustedt.wordpress.com
chris

6
@OMGtechy:これを引き起こす可能性はないと思います auto。以前の意味では、auto宣言には型名が必要です。新しい意味では、型名は許可されていません。
キース・トンプソン

2
どのようにオープンエンドですか?あなた自身が別の質問を通じて、この質問に対する答えは「はい、ここに方法の例を示しています」であると指摘しました。あなた自身が指摘したように、質問に対する非常に明確な答えがあります。
2014

回答:


283

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

  • 以前に暗黙的にコピーされたオブジェクトは、可能な場合は暗黙的にそれらを移動するようになりました。

マイナス面として、いくつかの例が規格の付録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では、リテラルのみが整数の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の右シフトとして解釈される場合があります。しかし、これは既存のコードを壊す可能性が高くなります:(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);
}

オペレーター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
} 

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はありません。したがって:

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

11
いいですね、+ 1。もう1つは、ユーザーが宣言したデストラクタが暗黙的にnoexecpt(true)存在するためthrow、デストラクタでが呼び出されることstd::terminateです。しかし、私はそのようなコードを書いた人なら誰でもこれに満足してくれることを願っています!
typ1232 14

4
しかし、std :: system_error自体は(間接的に)std :: exceptionから派生しているため、catch (std::exception &)まだキャッチされstd::ios_base::failureます。
user2665887 14

@ user2665887あなたは正しいです。それでもプログラムの動作に影響を与える可能性がありますが、今のところ最小限の例は思いつきません。
例:

4
あなたが言ったことoperator newは正確です(今はをスローすることができstd::bad_array_new_lengthます)ので、私は非常に混乱していますが、あなたの例ではまったくそれを示していません。表示するコードは、C ++ 03とC ++ 11 AFAIKで同じです。
Mooing Duck

2
list :: sizeのO(1)の裏側は、スプライスがO(n)になることです
Tony Delroy

55

この記事フォローアップを参照てください>>。両方でコンパイルしながら、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という警告フォームが表示されました。


必要としないこと奇妙であるtypenameため::two、C ++で03バージョン
ザヒール

3
いい基準です。それを煮詰めて、さまざまな基準に合わせて、trueまたはfalse異なる基準で評価します。多分私たちはそれを機能テストとして使用することができます</ joke>
cmaster-モニカを復活させます

@zahir、それは型ではなく単なる値です。
クリス

まあ、適切なcmdlineオプションはこれ(warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses])について警告しますが、あいまいな::演算子が意味を変える方法の良い例(グローバルスコープを参照するか、その直前にあるものを逆参照する)

@example、驚くべきことに、GCCはその警告を出しますが、Clangはしません。
クリス

39

はい、同じコードが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では、出現順に評価されます。したがってclang、C ++ 03モードでコンパイルすると、次の警告が表示されます(実際に表示されます)。

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.新しいシーケンスルールも++++ iを作成します。C ++ 11で明確に定義されている

C ++ 03以降に採用された新しい順序付け規則は、次のことを意味します。

int i = 0 ;
++++i ;

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

4.少し賢明な符号付き左シフト

C ++ 11の後のドラフトには、N3485以下にリンクするものが含まれており、1ビットを符号ビットにまたは符号ビットを超えてシフトする未定義の動作を修正しました。これは、不具合レポート1457でも取り上げられています。ハワード・ヒナントは、C ++ 11での左シフト(<<)は負の整数の未定義の動作であるというスレッドのこの変更の重要性についてコメントしています。

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

C ++ 11はconstexpr関数を導入しました:

constexpr指定子は、コンパイル時に関数または変数の値を評価できることを宣言します。このような変数と関数は、コンパイル時の定数式のみが許可されている場所で使用できます。

C ++ 03にはconstexpr機能がありませんが、標準ライブラリはC ++ 11でconstexprとして多くの関数を提供しているため、明示的にconstexprキーワードを使用する必要はありません。たとえば、std :: numeric_limits :: minです。たとえば、次のような異なる動作が発生する可能性があります。

#include <limits>

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

clangC ++ 03で使用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デストラクタ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が関数内で宣言された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、あなたが使用しない限り、このコードは、警告とC ++ 03モードで可能に-pedantic-errors一種不快のあるフラグを、それが生きてご覧ください

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は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コードは、この国際標準では異なる方法で実行される可能性があります。


それで、ほとんどの例は、以前は定義されていなかった振る舞いが現在は明確に定義されているという事実に絞り込んでいますか?
MatthiasB

@MatthiasB 2、3、および4はこれについてであり、現時点ではそれらは例の大多数ではありません。さらに多くの未定義の動作の例が見つかるとは思えないので、さらに追加すると、それらはより小さなセットになります。
Shafik Yaghmour 2014

まあ、#1の動作は指定されていないので、未定義の動作として数えます(少なくとも、c ++ 03では特定の結果が得られるとは期待できませんが、c ++ 11では可能です)。#5は、 C ++の標準拡張。しかし、私はあなたが正しいと思います。探すほど、両方の規格で定義されているが、異なる結果が得られる例が多くなります。
MatthiasB 14

@MatthiasBはい、未指定の動作と未定義の動作の両方で望ましくない結果が生じます。Linuxを考慮した拡張機能については、現実の世界ではそれらが重要であると想定する必要があるgcc拡張機能の数に依存しています。この質問に最初に回答したとき、私はそれほど多くの例を見つけるとは思っていませんでした。
Shafik Yaghmour 2014

35

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

次の例を検討してください(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の例

C ++ 11 Liveの例

その理由は、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つ目は以前と同様に機能しsizeprototype要素のコピーを作成します。ただし、最初の要素(指定されたサイズ引数のみを使用して呼び出しを処理する)は、デフォルトで各要素を個別に構築します。

この変更の理由についての私の推測は、C ++ 03オーバーロードは移動のみの要素タイプでは使用できないことです。しかし、それはそれでも画期的な変更であり、それが文書化されることはめったにありません。


3
これは明らかに重大な変更ですが、私はC ++ 11の動作を好みます。これにより、deque10個のウィジェットが同じリソースを共有するのではなく、10個の別個のウィジェットを保持することが期待されます。
Agentlien 2014

19

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

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

抽出に失敗すると、ゼロが書き込まれvaluefailbit設定されます。抽出の結果、値が大きすぎるか小さすぎてに収まらない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では、上記のコードは初期化されていない変数を使用しているため、動作が未定義です。


4
さらに、C ++ 03では、この標準化された動作を使用して、デフォルト値を提供することもできますint x = 1, y = 1; cin >> x >> y; cout << x*y;。C ++ 03では、これが読み取れxなかったときに正しく生成yされます。
cmaster-モニカを2014年

15

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

7

次に別の例を示します。

#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

コリルの結果を見る

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.