web-dev-qa-db-ja.com

std :: is_base_ofおよびstd :: enable_ifと組み合わせたC ++部分テンプレート特殊化

SerializablePrintableという2つのクラスがあるとします。

したがって、Printableのすべての派生クラスを受け入れる単純なテンプレート関数は次のようになります。

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

ただし、関数本体を引き続き制御しながらSerializableのすべての派生クラスも受け入れたい場合、これは明らかに機能しません。

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

template <class T, class B = Serializable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

// Error: Redefinition of ...

したがって、この問題の残りの解決策はテンプレートの特殊化であると考えました。

しかし、私はstd::is_base_ofおよびstd::enable_ifと組み合わせてテンプレートをどのように特殊化できるかを理解できません。

誰かが喜んで私を助けてくれることを願っています!

28
Tim

論理演算子を試してください:

std::enable_if<std::is_base_of<Serializable, T>::value ||
               std::is_base_of<Printable, T>::value>::type

あなたは簡単に可変式テンプレートを書くことができます:

is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value

例えば:

template <typename T, typename ...> struct is_base_of_any : std::true_type {};

template <typename T, typename Head, typename ...Rest>
struct is_base_of_any<T, Head, Rest...>
: std::integral_constant<bool, std::is_base_of<T, Head>::value ||
                               is_base_of_any<T, Rest...>::value>
{ };

異なる実装が必要な場合:

template <bool...> struct tag_type {};

template <typename T>
void foo(T, tag_type<true, false>) { }   // for Printable

template <typename T>
void foo(T, tag_type<false, true>) { }   // for Serializable

template <typename T>
void foo(T x)
{
    foo(x, tag_type<std::is_base_of<Printable, T>::value,
                    std::is_base_of<Serializable, T>::value>());
}

最後のオーバーロード(「ユーザー向け」のオーバーロード)は、おそらく上記のenable_ifオーバーロード候補を過度に作成しないようにします。

おそらく、可変個のtemplate <typename ...Bases>には次のようなタグを付けます:

tag_type<std::is_base_of<Bases, T>::value...>
22
Kerrek SB

Kerrekの回答より少し機械的ではありませんが、私はこれ以上読みにくいと思います:

template <class T, typename std::enable_if<std::is_base_of<Printable, T>::value>::type* = nullptr>
void print(const T& value) {
    std::cout << "printable(" << &value << ")\n";
}

template <class T, typename std::enable_if<std::is_base_of<Serializable, T>::value>::type* = nullptr>
void print(const T& value) {
    std::cout << "serializable(" << &value << ")\n";
}

それを見てください ideoneに住んでいます

21
Casey

このことを考慮:

void print(const Printable& value) {
    cout << value << endl;
}

void print(const Serializable& value) {
    cout << value << endl;
}

当然、適切なoperator<<右側のオペランドで仮想関数を呼び出すと、実際の印刷が行われます。

2
n.m.