23

好ましいのはこれです:

template<typename T>
bool isNotZero(const T &a)
{
    if (std::is_floating_point<T>::value) return abs(a) > std::numeric_limits<T>::epsilon();
    else return a;
}

またはこれ:?

template<typename T>
std::enable_if<std::is_floating_point<T>::value, bool>::type
isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<typename T>
std::enable_if<std::is_integral<T>::value, bool>::type
isNotZero(const T &a) { return a; }

関数の多くのバージョンを避けるために、通常は最初のタイプを使用します。

まったく同じだと思います。

オペコード段階で最適化された最初のバージョンと、テンプレートのインスタンス化段階での 2 番目のバージョン。

4

2 に答える 2

20

まったく同じだと思います。

まったく同じとは言えません。

最初のバージョンでは、実行時に評価される条件文を使用していますが、どのブランチを実行するかを決定する条件は、コンパイル時に決定できます。

したがって、コンパイル時に一方のみが実行され、もう一方は停止することがわかっていても、両方のブランチをコンパイルする必要があり、入力のタイプに関係なくコンパイルされます。コンパイラはここで警告を発します。

2 番目のケースでは、 input の型に適したものだけをコンパイル(そしてもちろん実行)します。私の意見では、これにより2番目のアプローチが優れています。

この特定の状況では、どちらのアプローチを選択してもおそらく違いはありませんが、コンパイル時の条件によって決定される条件付き実行は、コンパイル時のコンストラクト (SFINAE とテンプレートのオーバーロード) を使用して表現する必要があります。条件には whileifを使用する必要があります。システムの実行時の状態に依存します。

たとえば、条件分岐の 2 つの分岐に、対応する分岐が実行されたときにのみコンパイルされるコードが含まれている場合、最初のアプローチは不可能です。次の 2 つのタイプを検討してください。

struct X
{
    X(int) { }
};

struct Y
{
    Y() { }
};

そして、次の関数テンプレート:

template<typename T>
T foo(const T &a)
{
    if (std::is_constructible<T, int>::value)
    {
        return T(42);
    }
    else
    {
        return T();
    }
}

現在、次の呼び出しはどれも正当ではありません。

foo(X()); // ERROR! X is not default-constructible
foo(Y()); // ERROR! Y is not constructible from an int

これだけでも、一般に、コンパイル時の条件付き実行を処理するための適切なツールは、テンプレートのオーバーロード + SFINAE (またはクラス テンプレートの特殊化を含む可能性のある同等の構成要素) であることを示唆しています。

確かに、他のツールを使用できる退化したケース (この例など) はありますが、概念的に正しい設計ガイドラインを探しているのであれば、ここに明確な勝者があると思います。

もちろん、C++ にそのようなものがstatic if存在する場合は状況が異なりますが、現時点ではそうではなく、近い将来でさえそうではないようです。

于 2013-04-23T20:50:32.877 に答える
4

現在、私は SFINAE を使用したいと考えています。SFINAE を使用する場合、状況に応じて 1 つの関数のみを呼び出すことを明示的に許可しているため、最適化は必要ありません。実行時に適切な関数が決定なしで呼び出されるため、実行する最適化はありません。

条件ステートメントを使用するifと、実行時に決定を下す責任がプログラムにかかります。もちろん、これを最適化して取り除くことはできますが、そうではないかもしれません。特定のテンプレート引数に対してどちらが実際に実行されるかに関係なく、両方のブランチがコンパイルされる可能性があります。これは、各ブランチのコードが、特定のテンプレート引数に対して構文上および意味上正しい必要があることを意味します。

いつかstatic if、ステートメントをコンパイル時の条件として記述できるようになる日が来るかもしれませんが、現時点では、これについてifいくつかの強い感情があります。

C++ 向けに最近提案されたstatic if機能には根本的な欠陥があり、その採用は言語にとって災難となるでしょう。

ただし、近い将来 (C++14 周辺のリリースを目指して)、次のような関数を記述できるようにする制約(別名 concept-lite) がある可能性があります。

template<Floating_point T>
bool isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<Integral T>
bool isNotZero(const T &a) { return a; }

これは次のように書くことと同じです:

template<typename T>
  requires Floating_point<T>()
bool isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<typename T>
  requires Integral<T>()
bool isNotZero(const T &a) { return a; }

Floating_pointandIntegral制約は、コンパイル時にチェックされ、オーバーロードの解決に参加するテンプレート引数の単なる述語constexprです。これは、このような一連の関数を記述するための推奨される方法です。

于 2013-04-23T20:55:13.927 に答える