7
template<typename T> constexpr inline 
T getClamped(const T& mValue, const T& mMin, const T& mMax) 
{ 
     assert(mMin < mMax); // remove this line to successfully compile
     return mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue); 
}

エラー: constexpr 関数の本体'constexpr T getClamped(const T&, const T&, const T&) [with T = long unsigned int]'は return-statement ではありません

を使用しg++ 4.8.1ます。clang++ 3.4文句を言いません。

ここにいるのは誰?g++マクロを使わずにコードをコンパイルする方法はありますか?

4

4 に答える 4

13

GCCは正しいです。ただし、比較的簡単な回避策があります。

#include "assert.h"

inline void assert_helper( bool test ) {
  assert(test);
}
inline constexpr bool constexpr_assert( bool test ) {
  return test?true:(assert_helper(test),false);
}

template<typename T> constexpr
inline T getClamped(const T& mValue, const T& mMin, const T& mMax)
{
  return constexpr_assert(mMin < mMax), (mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue));
}

ここで、コンマ演算子を 2 回乱用します。

関数から呼び出すことができるassertwhenが必要なため、初めてです。2 つ目は、2 つの関数を 1 つの関数にチェーンできるようにするためです。trueconstexprconstexpr

副次的な利点として、式がコンパイル時にconstexpr_assert検証できない場合、関数は ではありません。truegetClampedconstexpr

が trueの場合assert_helper、 の内容assertは実装定義であるため存在するためNDEBUG、式に埋め込むことはできません (式ではなくステートメントである可能性があります)。また、失敗constexpr_assertした場合constexprでも失敗しないことを保証しassertますconstexpr(たとえば、いつNDEBUGが false)。

このすべての欠点は、アサートが問題が発生した行ではなく、2 つの呼び出しで発生することです。

于 2013-09-06T01:13:55.540 に答える
5

C++14 以降、これは問題ではなくなりました。g++フラグを使用する-std=c++14と、コードが正常にコンパイルおよび実行されます。

次の 3 つの欠点があります。

  • 質問で述べたように、これはC++11 では機能しません。
  • assertもちろん、コンパイル時にトリガーされることはありません。とは定数式とは見なされないstatic_assertため、同じ条件でa を追加しても機能しません。mMinmMax
  • さらに、assertはコンパイル時にトリガーされませんが、関数はconstexprであるため、条件がfalseで式コンパイル時に評価される場合 (例: constexpr auto foo = getClamped(1,2,0);)、assert決して起動しません。つまり、正しくない関数引数がキャッチされないことを意味します。 .

ユーザー oliora はコメントで、Eric Niebler による興味深いブログ投稿にリンクしています。この投稿では、C++11機能し、コンパイル中または実行時に必要に応じてトリガーできる複数のアプローチについて説明しています。

要するに、戦略は次のとおりです。

  • throw例外; キャッチできないようにするには (つまり のようにassert)、constexpr関数をマークします。nothrow
    • Niebler は彼の投稿でこれを呼び出していませんが、throw式は、三項式 (Niebler が彼の例で使用しているもの) など、edされる条件が である場合にのみ評価されるある種のより大きな論理式でラップする必要があります。C++14 であっても、スタンドアロンステートメントは許可されません。assertfalseif (condition) throw <exception>;
    • Niebler は、 とは異なりassert、このアプローチはに依存しないNDEBUGことにも注意を怠っています。リリース ビルド失敗とクラッシュを引き起こします。
  • コンストラクターが呼び出すカスタム式の型をスローしますstd::quick_exit。これにより、 の必要がなくなりnothrowます。
    • 繰り返しますが、これはリリース ビルド用にコンパイルされません (quick_exit呼び出しを でラップしない限りifdef)。
  • 任意のcallableassertを (テンプレート パラメーターとして) 受け取り、それを呼び出してから を呼び出しstd::quick_exit、次にthrowその構造体を呼び出す構造体に渡されます。これはやり過ぎのように思えますが、もちろん、実行時に真のアサーション失敗メッセージを生成します。これは素晴らしいことです。
    • これは、リリース ビルドがクラッシュしない唯一の方法です。
    • oliora は、 および を使用せずに、このアプローチのバリエーションを提供します。これははるかにクリーンで正気のようです。throwquick_exit
于 2016-10-06T21:00:55.710 に答える
1

g++ は正しいです。標準では、非静的はステートメントassertで許可されていません。constexpr


  ... その関数本体は、 null ステートメント、
  static_assert 宣言、
  クラスまたは列挙を定義しない typedef 宣言とエイリアス宣言、using
  宣言、
  using ディレクティブ、
  および正確に 1 つの returnのみを含む複合ステートメントでなければなりません。声明。
      -- 7.1.5/3

于 2013-09-06T00:42:12.687 に答える