【问题标题】:Specializing a template method with enable_if使用 enable_if 专门化模板方法
【发布时间】:2017-11-28 00:45:42
【问题描述】:

我正在编写一个存储std::function 的模板类,以便以后调用它。这是简化的代码:

template <typename T>
struct Test
{
    void call(T type)
    {
        function(type);
    }
    std::function<void(T)> function;
};

问题是这个模板不能为void类型编译因为

void call(void type)

变得未定义。

将它专门用于void 类型并不能缓解问题,因为

template <>
void Test<void>::call(void)
{
    function();
}

仍然与call(T Type)的声明不兼容。

所以,利用C++ 11的新特性,我尝试了std::enable_if

typename std::enable_if_t<std::is_void_v<T>, void> call()
{
    function();
}

typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
    function(type);
}

但它不能用 Visual Studio 编译:

错误 C2039:“类型”:不是“std::enable_if”的成员

你会如何解决这个问题?

【问题讨论】:

  • SFINAE 仅适用于 deduced 模板参数。

标签: c++ c++11 templates sfinae template-specialization


【解决方案1】:

专攻全班:

template <>
struct Test<void>
{
    void call()
    {
        function();
    }
    std::function<void()> function;
};

【讨论】:

  • 当然可以,但我们的想法是通过使用最近的 SFINAE 构造来精确避免对整个课程进行专业化。
【解决方案2】:

SFINAE 不能(仅)处理类/结构的模板参数。

适用于涉及方法模板参数的模板方法。

所以你必须写

   template <typename U = T>
   std::enable_if_t<std::is_void<U>::value> call()
    { function(); }

   template <typename U = T>
   std::enable_if_t<!std::is_void<U>::value> call(T type)
    { function(type); } 

或者,如果您想确定 UT

   template <typename U = T>
   std::enable_if_t<   std::is_same<U, T>::value
                    && std::is_void<U>::value> call()
    { function(); }

   template <typename U = T>
   std::enable_if_t<std::is_same<U, T>::value
                    && !std::is_void<U>::value> call(T type)
    { function(type); } 

p.s.:std::enable_if_t 是一种类型,因此之前不需要 typename

p.s.2:您标记了 C++11,但您的示例使用 std::enable_if_t,即 C++14,和 std::is_void_v,即 C++17

【讨论】:

  • 我没有完全理解为什么 SFINAE 在你的情况下工作而不是在我的情况下工作的微妙之处,但你的代码可以编译。感谢您提供额外的 cmets。
  • 对于生产级代码,您可能还希望在 enable_if_t 中使用 std::is_same_v&lt;T, U&gt; 来阻止显式 call&lt;U&gt;() for U 不等于 T 进行编译。
【解决方案3】:

如果你不坚持使用void,并且你的意图是真正能够在没有任何参数的情况下使用Test,那么使用可变参数模板:

template <typename ...T>
struct Test
{
    void call(T ...type)
    {
        function(type...);
    }
    std::function<void(T...)> function;
};

这样,您可以有任意数量的参数。如果你不想有参数,使用这个:

Test<> t;
t.call();

所以这不是你想要的语法,但不需要专门化,而且这种解决方案更灵活,因为支持任意数量的参数。

【讨论】:

  • 我给出了代码的简化版本,但实际上这个想法是处理一个 void 返回类型。我按照您的建议使用可变参数模板处理了输入参数。
猜你喜欢
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
相关资源
最近更新 更多