在运行时可以检测到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
这个练习的目的是什么?首先,您确实有一个宏,其次,编译器开始实现C ++ 0x的所有功能将花费很多年,与此同时,它是一个混合体。因此,唯一合理的测试是编译器的版本宏。
Gene Bushuyev 2011年

4
这不是一个真正的问题,但似乎太有趣了,不能遵循规则!
David Heffernan

4
@Gene等人:您是否否决所有有趣的问题,但看不到务实的“观点”?
Armen Tsirunyan 2011年

2
“我们希望答案通常包含事实,参考或特定专业知识”。我认为这个问题符合这些期望,请重新投票。
卡雷尔·佩特拉内克

6
@sixlettervariables:虽然可以肯定措辞可能会更好,但在我看来,问题的基本概念(在C ++ 03和C ++ 0x之间可以在运行时检测到什么差异,时间?)是完全合法的。鉴于代码必须同时进行编译和执行,因此也可以将其表述为C ++ 0x的重大变化。在我看来,这似乎也是一个完全合理的问题。
杰里·科芬,

Answers:


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模式。c声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
真好 我可以确认此解决方案在g ++(GCC)4.6.0的支持下,无论是否带有-std = c ++ 0x都可以使用。
亚历山大

2
这将返回true2005年的MSVC开始,并在2003年MSVC编译错误
安东尼·威廉姆斯

1
哦,亲爱的,他们打破了向后兼容性!
avakar 2011年

14
@Johannes:这是您数周以来最有趣的,不是吗?;-]
ildjarn 2011年

4
我发现这些都很有趣,但是我认为最聪明的是(...)vs (char*)通话。我真的很喜欢
corsiKa 2011年

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 2011年

1
好吧,如果在函数#undef u8后面加上a,则只有在程序具有一个预先定义的宏u8(boooo)时,才能观察到预处理器的使用情况。如果这是一个真正的问题,仍然可以使用特定于实现的push / pop宏编译指示/调用来解决(我相信大多数实现都有这些实现)。
James McNellis 2011年

3
一个相当合理的论据是,在C ++ 03系统上,可能有人#define u8以提供模拟的C ++ 0x功能。不过,我还是很喜欢这个答案。
Christopher Smith,

1
您可以只将isCpp0x函数移至单独的翻译单元,以使此宏内容不会影响其他代码。
unkulunkulu 2011年

1
我认为使用预处理器依靠编译器设置一些宏值与使用预处理器检测实际语言功能之间是有区别的。这就是为什么我认为这个答案没有作弊。
Lightness Races in Orbit Race's

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但允许>>在模板中以C ++ 0x方式使用的Visual C ++ 2005/2088一起中断。
卡雷尔·佩特拉内克

4
噢!我喜欢滥用ADL!但是,合格的C ++ 03实现不能具有名为的函数std::move吗?
James McNellis 2011年

1
@FredOverflow:我不会打扰。UI很烂!
Lightness Races in Orbit

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 2011年

1
+1好主意,我不确定是否isC++0x是有效的C ++标识符;)
Karel Petranek 2011年

1
从引用推断中获取参考的最佳参考是什么?
Kerrek SB 2011年

@ kerrek-sb:草案在
8.3.2.6

15

这不是一个正确的示例,但它是一个有趣的示例,可以区分C与C ++ 0x(尽管它是无效的C ++ 03):

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

10
从技术上讲,这取决于sizeof(int) != 1事实。在chars 非常大的0x系统上,结果可能是相同的。不过,这仍然是一个巧妙的把戏。
丹尼斯·齐克福斯

@Dennis- char尽管始终为一个字节
节点

4
@Node:一个字节并不总是8位。
Alexandre C.

2
sizeof(char)根据定义,@ Node 将始终为1。但CHAR_BIT(limits.h中所定义的)允许是大于8。其结果是,既charint可能有32位,在这种情况下sizeof(int) == 1(和CHAR_BIT == 32)。
Sjoerd

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.