C ++ 03とC ++ 11の間に、実行時に検出できる違いがあるとすれば、どのようなものですか?


116

Cコンパイラーでコンパイルすると0を返し、C ++コンパイラーでコンパイルすると1を返す関数を書くことができます(取るに足らない解決策#ifdef __cplusplusは興味深いもので はありません)。

例えば:

int isCPP()
{
    return sizeof(char) == sizeof 'c';
}

もちろん、上記がとsizeof (char)同じでない場合にのみ機能しますsizeof (int)

別のよりポータブルなソリューションは次のようなものです:

int isCPP()
{
    typedef int T;
    {
       struct T 
       {
           int a[2];
       };
       return sizeof(T) == sizeof(struct T);
    }
}

例が100%正しいかどうかはわかりませんが、あなたは考えを理解しています。同じ関数を書く方法は他にもあると思います。

C ++ 03とC ++ 11の間に、実行時に検出できる違いがあるとすれば、どのようなものですか?言い換えると、それが適合C ++ 03コンパイラーによってコンパイルされたかC ++ 11コンパイラーによってコンパイルされたかを示すブール値を返す同様の関数を作成することは可能ですか?

bool isCpp11()
{ 
    //???
} 

10
そして、この演習のポイントは何ですか?1つ目はマクロです。2つ目は、コンパイラーがC ++ 0xのすべての機能の実装を開始するまでに数年かかります。したがって、唯一の妥当なテストはコンパイラとバージョンマクロです。
ジーンブシュエフ

4
これは実際の質問ではない法案に適合しますが、ルールに従うのは面白すぎるようです!
David Heffernan

4
@Gene et al:興味深い質問はすべて反対票を投じますが、実用的な「ポイント」は表示されませんか?
Armen Tsirunyan

2
「私達は答えが一般に事実、参照、または特定の専門知識を含むことを期待します」。この質問はこれらの期待を満たしていると思います。再開に投票してください。
Karel Petranek

6
@sixlettervariables:言い回しの方が優れている可能性があることは間違いなく議論の余地がありますが、質問の基本的な概念(C ++ 03とC ++ 0xの違いは、実行時に検出できると思われます)時間?)完全に正当です。コードは両方でコンパイルして実行する必要があることを考えると、C ++ 0xの重大な変更に関するものと表現することもできます。それは私にとっても、完全に正当な質問のように思えます。
ジェリーコフィン

回答:


108

コア言語

を使用して列挙子にアクセスする::

template<int> struct int_ { };

template<typename T> bool isCpp0xImpl(int_<T::X>*) { return true; }
template<typename T> bool isCpp0xImpl(...) { return false; }

enum A { X };
bool isCpp0x() {
  return isCpp0xImpl<A>(0);
}

新しいキーワードを悪用することもできます

struct a { };
struct b { a a1, a2; };

struct c : a {
  static b constexpr (a());
};

bool isCpp0x() {
  return (sizeof c::a()) == sizeof(b);
}

また、文字列リテラルがもう変換されないという事実 char*

bool isCpp0xImpl(...) { return true; }
bool isCpp0xImpl(char*) { return false; }

bool isCpp0x() { return isCpp0xImpl(""); }

しかし、実際の実装でこれが機能する可能性がどれほどあるかはわかりません。悪用するものauto

struct x { x(int z = 0):z(z) { } int z; } y(1);

bool isCpp0x() {
  auto x(y);
  return (y.z == 1);
}

以下は、C ++ 0xではoperator int&&への変換関数であり、C ++ 03では論理積int&&int後に続く変換であるという事実に基づいています。

struct Y { bool x1, x2; };

struct A {
  operator int();
  template<typename T> operator T();
  bool operator+();
} a;

Y operator+(bool, A);

bool isCpp0x() {
  return sizeof(&A::operator int&& +a) == sizeof(Y);
}

このテストケースは、GCCのC ++ 0xでは機能せず(バグのように見えます)、clangのC ++ 03モードでは機能しません。clang PRが提出されました

C ++ 11でテンプレートの注入されたクラス名変更された処理

template<typename T>
bool g(long) { return false; }

template<template<typename> class>
bool g(int) { return true; }

template<typename T>
struct A {
  static bool doIt() {
    return g<A>(0);
  }
};

bool isCpp0x() {
  return A<void>::doIt();
}

いくつかの「これがC ++ 03かC ++ 0xかを検出する」を使用して、重大な変更を示すことができます。以下は、最初はそのような変更を示すために使用されていましたが、現在はC ++ 0xまたはC ++ 03のテストに使用されている微調整されたテストケースです。

struct X { };
struct Y { X x1, x2; };

struct A { static X B(int); };
typedef A B;

struct C : A {
  using ::B::B; // (inheriting constructor in c++0x)
  static Y B(...);
};

bool isCpp0x() { return (sizeof C::B(0)) == sizeof(Y); }

標準ライブラリ

operator void*C ++ 0xの欠如の検出'std::basic_ios

struct E { E(std::ostream &) { } };

template<typename T>
bool isCpp0xImpl(E, T) { return true; }
bool isCpp0xImpl(void*, int) { return false; }

bool isCpp0x() {
  return isCpp0xImpl(std::cout, 0);
}

1
いいね。このソリューションが、gst(GCC)4.6.0で-std = c ++ 0xの有無にかかわらず動作することを確認できます。
アレクサンダー

2
この戻りtrue以降MSVC 2005年、および2003年MSVCでコンパイルエラー
アンソニー・ウィリアムズ

1
ああ、彼らは後方互換性を壊しました!
avakar 2011年

14
@ヨハネス:これはあなたが数週間で持っていた中で最も楽しいですよね?;-]
ildjarn、2011年

4
これらはすべて興味深いものですが、最も賢いのは(...)vs (char*)呼び出しです。本当に好きです!
corsiKa

44

C ++ 11で導入された重大な変更からインスピレーションを得ましたか?

#define u8 "abc"

bool isCpp0x() {
   const std::string s = u8"def"; // Previously "abcdef", now "def"
   return s == "def";
}

これは、マクロ展開よりも優先される新しい文字列リテラルに基づいています。


1
+1:非常に興味深いですが、実際にはプリプロセッサを使用しないという要件を技術的に破っています。しかし、制限はそのような素晴らしい答えを却下することを意図していなかった:)
Armen Tsirunyan '24

1
ええと、関数の#undef u8後にa を付けた場合、プログラムにu8(boooo)という名前の以前に定義されたマクロがある場合にのみ、プリプロセッサの使用状況を確認できます。それが本当の懸念である場合、実装固有のプッシュ/ポップマクロプラグマ/呼び出しを使用して回避できます(ほとんどの実装にはこれらがあると思います)。
James McNellis、2011年

3
かなり合理的な議論の1つは、C ++ 03システムでは、シミュレートされたC ++ 0x機能を提供するために誰かがu8を#defineする可能性があるということです。それでも、私は答えが本当に好きです。
Christopher Smith

1
このisCpp0x関数を別の変換単位に移動するだけで、このマクロが他のコードに影響を与えることがなくなります。
unkulunkulu

1
プリプロセッサーを使用してコンパイラーがマクロ値を設定することに依存することと、プリプロセッサーを使用して実際の言語機能を検出することには違いがあると思います。だから私はこの答えが浮気だとは思わない。
軌道の軽さのレース

33

>>テンプレートを閉じるための新しいルールを使用したチェックはどうですか?

#include <iostream>

const unsigned reallyIsCpp0x=1;
const unsigned isNotCpp0x=0;

template<unsigned>
struct isCpp0xImpl2
{
    typedef unsigned isNotCpp0x;
};

template<typename>
struct isCpp0xImpl
{
    static unsigned const reallyIsCpp0x=0x8000;
    static unsigned const isNotCpp0x=0;
};

bool isCpp0x() {
    unsigned const dummy=0x8000;
    return isCpp0xImpl<isCpp0xImpl2<dummy>>::reallyIsCpp0x > ::isNotCpp0x>::isNotCpp0x;
}

int main()
{
    std::cout<<isCpp0x()<<std::endl;
}

または、以下を簡単に確認しstd::moveます。

struct any
{
    template<typename T>
    any(T const&)
    {}
};

int move(any)
{
    return 42;
}

bool is_int(int const&)
{
    return true;
}

bool is_int(any)
{
    return false;
}


bool isCpp0x() {
    std::vector<int> v;
    return !is_int(move(v));
}

6
+1確かにクールなアイデア:)しかし、実際には、これはC ++ 0xをサポートしないVisual C ++ 2005/2088で壊れますが、>>をテンプレートでC ++ 0xの方法で使用できます。
Karel Petranek

4
うーん ADLの悪用が好きです。ただし、準拠するC ++ 03実装には、std::move
James McNellis、2011年

1
@FredOverflow:気にしません。UIはひどい!
オービットのライトネスレース2013年

16

以前のC ++とは異なり、C ++ 0xでは、たとえばテンプレートパラメーターを通じてその基本参照型が導入されている場合、参照型から参照型を作成できます。

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

bool isCpp0x() 
{
    int v = 1;
    return func<int&>(v); 
}

申し訳ありませんが、完全な転送には、下位互換性を損なうという代償があります。

別のテストは、テンプレート引数として現在許可されているローカルタイプに基づくことができます。

template <class T> bool cpp0X(T)  {return true;} //cannot be called with local types in C++03
                   bool cpp0X(...){return false;}

bool isCpp0x() 
{
   struct local {} var;
   return cpp0X(var);
}

多分私はそれを特性クラスに変えたはずです;)
uwedolinsky

1
+1いい考えですisC++0xが、それが有効なC ++識別子であるかどうかは
わかり

1
参照推論からの参照に適した参照は何ですか?
Kerrek SB、2011

@ kerrek-sb:ドラフトは8.3.2.6(参照)でこれについて話します
uwedolinsky

15

これは完全に正しい例ではありませんが、CとC ++ 0xを区別できる興味深い例です(ただし、C ++ 03は無効です)。

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}

10
技術的には、これはsizeof(int) != 1真実であることに依存しています。chars が非常に大きい0xシステムでは、結果は同じになる可能性があります。それでも、きちんとしたトリックです。
Dennis Zickefoose 2011年

@Dennis- char常に1バイトですが
ノード

4
@ノード:1バイトは常に8ビットではありません。
アレクサンドルC.

2
sizeof(char)定義により、@ Node は常に1になります。ただし、CHAR_BIT(limits.hで定義されている)8を超えることは許可されています。その結果、charおよびintは32ビットになる可能性があり、その場合sizeof(int) == 1(およびCHAR_BIT == 32)です。
Sjoerd、2011年

12

この質問から:

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

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;

これがどのように機能するのか疑問に思いました。試してみると、今では明らかです。小さなバグがあります。:あなたが最後の行を変更した場合、それは動作しますbool is_cpp0x = !test[0].flag;
AWX

1
もっともらしい:C ++ 0xのデフォルトの構成要素TがC ++ 03から構成要素をコピーするT()
awx '26 / 08/26

9

それほど簡潔ではありませんが...現在のC ++では、クラステンプレート名自体が、そのクラステンプレートのスコープ内で(テンプレート名ではなく)タイプ名として解釈されます。一方、C ++ 0x(N3290 14.6.1 / 1)では、テンプレート名としてクラステンプレート名を使用できます。

template< template< class > class > char f( int );
template< class > char (&f(...))[2];

template< class > class A {
  char i[ sizeof f< A >(0) ];
};

bool isCpp0x() {
  return sizeof( A<int> ) == 1;
}

9
#include <utility>

template<typename T> void test(T t) { t.first = false; }

bool isCpp0x()
{
   bool b = true;
   test( std::make_pair<bool&>(b, 0) );
   return b;
}

技術的には、これはコンパイラではなく標準ライブラリをテストします。これは有効なC ++ 03および有効なC ++ 0xですが、有効なC ++ 98ではないため、C ++ 98 /を検出するためにいくつかの微調整を行うことができます。 C ++ 03 / C ++ 0x stdlib
Jonathan Wakely
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.