【问题标题】:C++ partial template specialization in combination with std::is_base_of and std::enable_ifC++ 部分模板特化结合 std::is_base_of 和 std::enable_if
【发布时间】:2013-06-19 20:32:54
【问题描述】:

假设我有两个类:SerializablePrintable

所以一个接受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_ofstd::enable_if 结合使用。

希望有人愿意帮助我!

【问题讨论】:

  • 因此,如果调用者没有为B 指定任何内容,您希望T 派生自Printable 还是派生自Serializable
  • 看来您可以简单地使用两个非模板重载:void print(const Printable&amp; value);void print(const Serializable&amp; value);
  • @KennyTM 我可以,但我需要它作为模板函数。
  • @Tim:为什么需要模板?
  • @AndyProwl 不,class B = ... 并不是可选的,我只是为了便于阅读而添加它。

标签: templates c++11 specialization


【解决方案1】:

尝试逻辑运算符:

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 &lt;typename ...Bases&gt;

tag_type<std::is_base_of<Bases, T>::value...>

【讨论】:

  • 我明白,但我希望能够根据基类控制每个函数的主体。
  • @Tim:哦,好的,我明白了。在这种情况下,您需要单独的重载和调度辅助函数;我会说某种标签调度。
  • 谢谢。我会调查的!
  • class ControlMeHarder: public Serializable, public Printable {};.
  • @n.m.:量子超载!
【解决方案2】:

比 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";
}

live at ideone

【讨论】:

  • 这实际上是我的问题的最佳解决方案,因为代码是机器生成的。希望我能接受两个答案!谢谢!
  • @KerrekSB 确实如此。但我认为您的解决方案更适合有相同问题的其他人。
  • @Casey 有没有办法明确选择重载编译时间?喜欢print&lt;Derived, Base&gt;(derived)
  • @Nik-Lz 只有第一个模板参数T 是类型参数。在这两种情况下,第二个模板参数都是在满足enable_if 条件时类型为指向无效的非类型参数 - 否则是指向替换失败的指针 - 其值默认为 nullptr
  • @Nik-Lz 未命名的第二个参数的唯一目的是在T 不满足重载要求时导致替换失败,从而有效地从这种情况下的重载集中删除该重载.这里的意图是我们有两个相同的重载 print one of which will be selected by overload resolution when the type of the function parameter derives from Printable, and another which will be selected the the type of the function parameter derives from Serializable`。
【解决方案3】:

考虑一下:

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

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

当然,您将在右侧操作数中调用适当的operator&lt;&lt; 虚函数,该函数将执行实际打印。

【讨论】:

    猜你喜欢
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    相关资源
    最近更新 更多