13

ほとんどのC++プログラマーが知っておくべきことですが、フリー関数の部分的なテンプレートの特殊化は許可されていません。たとえば、以下は不正なC++です。

template <class T, int N>
T mul(const T& x) { return x * N; }

template <class T>
T mul<T, 0>(const T& x) { return T(0); }

// error: function template partial specialization ‘mul<T, 0>’ is not allowed

ただし、クラス/構造体の部分的なテンプレートの特殊化許可されており、無料の関数の部分的なテンプレートの特殊化の機能を模倣するために利用できます。たとえば、最後の例の目標は、次を使用して達成できます。

template <class T, int N>
struct mul_impl
{
    static T fun(const T& x) { return x * N; }
};

template <class T>
struct mul_impl<T, 0>
{
    static T fun(const T& x) { return T(0); }
};

template <class T, int N>
T mul(const T& x)
{
    return mul_impl<T, N>::fun(x);
}

それはよりかさばり、簡潔ではありませんが、それは仕事を成し遂げます-そしてユーザーmulに関する限り、彼らは望ましい部分的な専門化を取得します。


私の質問は次のとおりです。テンプレート化された無料の関数(他の人が使用することを目的としています)を作成する場合、実装をクラスの静的メソッド関数に自動的に委任して、ライブラリのユーザーが部分的な特殊化を自由に実装できるようにする必要があります。テンプレート化された関数を通常の方法で記述し、人々がそれらを専門化できないという事実を受け入れますか?

4

2 に答える 2

3

litbが言うように、ADLは、それが機能する場合に優れています。これは、基本的に、テンプレートパラメーターが呼び出しパラメーターから推測できる場合に使用されます。

#include <iostream>

namespace arithmetic {
    template <class T, class S>
    T mul(const T& x, const S& y) { return x * y; }
}

namespace ns {
    class Identity {};

    // this is how we write a special mul
    template <class T>
    T mul(const T& x, const Identity&) {
        std::cout << "ADL works!\n";
        return x;
    }

    // this is just for illustration, so that the default mul compiles
    int operator*(int x, const Identity&) {
        std::cout << "No ADL!\n";
        return x;
    }
}

int main() {
    using arithmetic::mul;
    std::cout << mul(3, ns::Identity()) << "\n";
    std::cout << arithmetic::mul(5, ns::Identity());
}

出力:

ADL works!
3
No ADL!
5

arithmetic::mulOverloading + ADLは、の関数テンプレートを部分的に特殊化することで達成できることを実現しますS = ns::Identity。ただし、ADLを許可する方法で呼び出すことは呼び出し元に依存しているため、std::swap明示的に呼び出すことはありません。

つまり、問題は、ライブラリのユーザーが関数テンプレートを部分的に特殊化する必要があると予想することです。タイプに特化する場合(アルゴリズムテンプレートの場合のように)、ADLを使用します。あなたの例のように、それらを整数テンプレートパラメータに特化する場合は、クラスに委任する必要があると思います。しかし、私は通常、3による乗算が何をすべきかをサードパーティが定義することを期待していません-私のライブラリはすべての整数を実行します。八元数による乗算が何をするかを第三者が定義することを合理的に期待できます。

考えてみると、私arithmetic::mulはと紛らわしいほど似ているので、べき乗は私が使用するためのより良い例だったかもしれません。したがって、実際に私の例operator*に特化する必要はありません。mul次に、「何かの力に対するアイデンティティはアイデンティティである」ため、最初のパラメータに特化/ADLオーバーロードします。うまくいけば、あなたはアイデアを得るでしょう。

ADLには欠点があると思います。名前空間を効果的にフラット化します。arithmetic::subADLを使用して、クラスとクラスの両方を「実装」したい場合はsandwich::sub、問題が発生する可能性があります。専門家がそれについて何と言っているのかわかりません。

つまり:

namespace arithmetic {
    // subtraction, returns the difference of lhs and rhs
    template<typename T>
    const T sub(const T&lhs, const T&rhs) { return lhs - rhs; }
}

namespace sandwich {
    // sandwich factory, returns a baguette containing lhs and rhs
    template<typename SandwichFilling>
    const Baguette sub(const SandwichFilling&lhs, const SandwichFilling&rhs) { 
      // does something or other 
    }
}

今、私はタイプを持っていns::HeapOfHamます。std :: swapスタイルのADLを利用して、arithmetic :: sub:の独自の実装を作成したいと思います。

namespace ns {
    HeapOfHam sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
        assert(lhs.size >= rhs.size && "No such thing as negative ham!");
        return HeapOfHam(lhs.size - rhs.size);
    }
}

また、std :: swapスタイルのADLを利用して、sandwich::subの独自の実装を作成したいと思います。

namespace ns {
    const sandwich::Baguette sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
        // create a baguette, and put *two* heaps of ham in it, more efficiently
        // than the default implementation could because of some special
        // property of heaps of ham.
    }
}

ちょっと待ってください。できませんね。同じパラメータと異なるリターンタイプを持つ異なる名前空間の2つの異なる関数:通常は問題ではありません。それが名前空間の目的です。しかし、両方をADL化することはできません。おそらく私は本当に明白な何かを見逃しています。

ところで、この場合、私はそれぞれとを完全に専門化することができarithmetic::subますsandwich::sub。発信者はusingどちらか一方になり、適切な機能を取得します。ただし、元の質問は部分的な特殊化について説明しているので、実際にHeapOfHamをクラステンプレートにしない限り、特殊化はオプションではないふりをすることができますか?

于 2010-03-08T23:03:14.993 に答える
1

他の場所や他の人が使用するライブラリを作成している場合は、構造体/クラスのことを行います。それはより多くのコードですが、ライブラリのユーザー (おそらく将来のあなた!) に感謝します。これが 1 つの使用コードである場合、部分的な特殊化が失われても害はありません。

于 2010-03-08T19:33:15.477 に答える