38

pureは、関数がグローバル メモリを変更しないことを示す関数属性です。
constは、関数がグローバル メモリを読み取ったり変更したりしないことを示す関数属性です。

その情報があれば、コンパイラはいくつかの追加の最適化を行うことができます。

GCC の例:

float sigmoid(float x) __attribute__ ((const));

float calculate(float x, unsigned int C) {
    float sum = 0;
    for(unsigned int i = 0; i < C; ++i)
        sum += sigmoid(x);
    return sum;
}

float sigmoid(float x) { return 1.0f / (1.0f - exp(-x)); }

その例では、コンパイラは関数calculateを次のように最適化できます。

float calculate(float x, unsigned int C) {
    float sum = 0;
    float temp = C ? sigmoid(x) : 0.0f;
    for(unsigned int i = 0; i < C; ++i)
        sum += temp;
    return sum;
}

または、コンパイラが十分に賢い場合 (フロートについてそれほど厳密ではない場合):

float calculate(float x, unsigned int C) { return C ? sigmoid(x) * C : 0.0f; }

GCC、Clang、ICC、MSVC などのさまざまなコンパイラに対して、このような方法で関数をマークするにはどうすればよいですか?

4

3 に答える 3

28

一般に、ほとんどすべてのコンパイラが GCC 属性をサポートしているようです。MSVC は、これまでのところ、それらをサポートしていない唯一のコンパイラです (また、代替手段もありません)。

于 2010-05-09T17:32:00.057 に答える
12

まず、「const」は「pure」のより厳密なバージョンであるため、コンパイラが「const」を実装していない場合、「pure」をフォールバックとして使用できることに注意してください。

他の人が述べたように、MSVC には実際には似たようなものはありませんが、多くのコンパイラが GCC 構文を採用しています__GNUC__

  • バージョンを確認したい場合に備えて、 GCC は 2.96 以降の pure と 2.5.0 以降の const をサポートして ます
  • Clang は両方をサポートします。と を使用__has_attribute(pure)__has_attribute(const)てそれらを検出できますが、おそらく clang 設定だけに頼っても問題ありません__GNUC__。これには、emscripten や XL C/C++ 13+ などの clang に基づくコンパイラーも含まれます。
  • Intel C/C++ Compiler は両方をサポートしていますが、ドキュメントがひどいので、いつ追加されたのかわかりません。16.0+ は確かに安全です。
  • Oracle Developer Studio 12.2+は両方をサポートします。
  • ARM C/C++ コンパイラ 4.1+ (およびおそらくそれより古い) は、 pureconstの両方をサポートします。
  • 少なくとも10.1以降の IBM XL C/C++ 。
  • TI 8.0+
  • --gcc を指定したTI 7.3+ ( で検出__TI_GNU_ATTRIBUTE_SUPPORT__) は両方をサポートします。
  • PGIはそれを文書化していませんが(AFAICT)、両方の属性が機能します(または少なくとも静かに無視されます)。17.10+ は安全ですが、おそらくずっと以前から受け入れられていました。

これらのうち、clang は常に__GNUC__および フレンドを定義します (現在は 4.2、IIRC まで)。Intel は__GNUC__デフォルトで (-no-gcc で抑制することができますが) C++ モードで PGI を定義します (C モードでは定義しません)。それ以外は手動で確認する必要があります。

Oracle Developer Studio は、 Forte Developer 6として知られて以来、プラグマもサポートしています。関数名を指定する必要があるため、使用方法が少し異なります。

/* pure: */
#pragma does_not_write_global_data (funcname)
/* const; SPARC-only until 12.2 */
#pragma no_side_effect (funcname)

TI 6.0+ (少なくとも) は、#pragma FUNC_IS_PURE;C++ モードでのみプラグマをサポートします。Cモードでは、#pragma FUNC_IS_PURE(funcname);.

これのほとんどは、マクロの背後に隠すことができます。これは、私が Hedleyで行ったことです。

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(pure,2,96,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_PURE __attribute__((__pure__))
#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
#  define HEDLEY_NO_RETURN _Pragma("FUNC_IS_PURE;")
#else
#  define HEDLEY_PURE
#endif

#if HEDLEY_GNUC_HAS_ATTRIBUTE(const, 2, 5, 0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_CONST __attribute__((__const__))
#else
#  define HEDLEY_CONST HEDLEY_PURE
#endif

これには、関数名を引数として必要とするバリアントは含まれていませんが、依然として大多数のユーザーをカバーしており、どこでも安全に使用できます。

Hedley (単一のパブリック ドメイン/CC0 ヘッダー) を使用したくない場合は、内部バージョンのマクロを置き換えるのは難しくありません。そうすることを選択した場合は、この回答ではなく、Hedley リポジトリに基づいてポートを作成する必要があります。これは、最新の状態に保つ可能性が高いためです。

于 2018-03-24T19:13:03.753 に答える