【问题标题】:What differences, if any, between C++03 and C++11 can be detected at run-time?在运行时可以检测到 C++03 和 C++11 之间的哪些差异(如果有)?
【发布时间】:2011-09-22 07:50:20
【问题描述】:

可以编写一个函数,使用 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()
{ 
    //???
} 

【问题讨论】:

  • 这个练习的意义何在?首先,您确实有一个宏,其次,编译器开始实现 C++0x 的所有功能需要数年时间,同时它将是一个混合体。所以唯一合理的测试是编译器一个版本宏。
  • 这符合不是一个真正的问题,但遵循规则似乎太有趣了!
  • @Gene 等人:您是否对所有有趣但看不到实用“要点”的问题投反对票?
  • “我们希望答案通常涉及事实、参考资料或特定专业知识”。我认为这个问题符合这些期望,投票支持重新开放。
  • @sixlettervariables:虽然措辞可能更好,但在我看来,这个问题的基本概念(如果有的话,C++03 和 C+ +0x 可以在运行时检测到?)是完全合法的。鉴于代码必须在两者中编译和执行,它也可以被表述为关于 C++0x 中的重大变化。在我看来,这也是一个完全合理的问题。

标签: c++ c++11 c++03 language-detection


【解决方案1】:

核心语言

使用::访问枚举器:

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

以下基于operator int&amp;&amp;是C++0x中int&amp;&amp;的转换函数,C++03中int后跟逻辑-and的转换

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(看起来像一个错误),并且不适用于 C++03 模式的 clang。 A clang PR has been filed.

C++11 模板的modified treatment of injected class names

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

标准库

检测 C++0x 中缺少operator void*'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);
}

【讨论】:

  • 不错。我可以确认此解决方案适用于 g++ (GCC) 4.6.0,无论有无 -std=c++0x。
  • 这将返回 true 用于 MSVC 2005 及更高版本,并在 MSVC 2003 中返回编译错误。
  • 天哪,它们破坏了向后兼容性!
  • @Johannes :这是你几周以来最开心的一次,不是吗? ;-]
  • 我觉得这些都很有趣,但我认为最聪明的是(...) vs (char*) 调用。我真的很喜欢!
【解决方案2】:

我的灵感来自What breaking changes are introduced in C++11?

#define u8 "abc"

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

这是基于优先于宏扩展的新字符串文字。

【讨论】:

  • +1:确实很有趣,但在技术上打破了不使用预处理器的要求。但是这个限制并不是要拒绝这样好的答案:)
  • 好吧,如果你在函数后面加上#undef u8,那么预处理器的使用只有在你的程序有一个先前定义的名为u8的宏时才能观察到(嘘)。如果这是一个真正的问题,仍然可以使用特定于实现的 push/pop 宏 pragma/calls 来解决这个问题(我相信大多数实现都有这些)。
  • 一个相当合理的论点是,在 C++03 系统上,有人可能会 #define u8 来提供模拟的 C++0x 功能。不过,我真的很喜欢这个答案。
  • 你可以把这个 isCpp0x 函数移动到一个单独的翻译单元,这样这个宏就不会影响其他代码了。
  • 我认为使用预处理器依赖编译器设置一些宏值与使用预处理器检测实际语言特征之间存在差异。这就是为什么我不认为这个答案是作弊。
【解决方案3】:

如何使用&gt;&gt;关闭模板的新规则进行检查:

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

【讨论】:

  • +1 确实是一个很酷的想法 :) 但是,实际上这将与不支持 C++0x 但允许 >> 在 C++ 模板中使用的 Visual C++ 2005/2088 中断0x 方式。
  • 哦;我喜欢 ADL 滥用!但是,一个符合标准的 C++03 实现不能有一个名为 std::move 的函数吗?
  • @FredOverflow:我不会打扰。用户界面糟透了!
【解决方案4】:

与之前的 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);
}

【讨论】:

  • 也许我应该把它变成一个特质类;)
  • +1 好主意,我只是不确定isC++0x 是否是一个有效的 C++ 标识符;)
  • 从references inference中引用什么是好的参考?
  • @kerrek-sb:草案在 8.3.2.6(参考)中讨论了这一点
【解决方案5】:

这不是一个完全正确的例子,但它是一个有趣的例子,可以区分 C 和 C++0x(虽然它是无效的 C++03):

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

【讨论】:

  • 从技术上讲,这依赖于sizeof(int) != 1 的真实性。在具有异常大的chars 的 0x 系统上,结果可能是相同的。不过,这仍然是一个巧妙的技巧。
  • @Dennis - char 总是一个字节
  • @Node:一个字节并不总是8位。
  • @Node sizeof(char) 根据定义将始终为 1。但是CHAR_BIT(在limits.h 中定义)允许大于8。因此,charint 都可以有32 位,在这种情况下sizeof(int) == 1(和CHAR_BIT == 32)。
【解决方案6】:

来自this question

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;
  • 似是而非:C++0x 默认构造 T 而 C++03 复制构造来自 T()
【解决方案7】:

虽然不是那么简洁... 在当前的 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;
}

【讨论】:

    【解决方案8】:
    #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 标准库
    猜你喜欢
    • 2018-03-20
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 2017-04-15
    • 2015-02-19
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    相关资源
    最近更新 更多