【问题标题】:C++ SFINAE enable_if_t in member function, how to disambiguate?成员函数中的 C++ SFINAE enable_if_t,如何消除歧义?
【发布时间】:2018-08-29 18:19:05
【问题描述】:

假设我们有一些 SFINAE 成员函数:

class foo{
    template <class S, class = std::enable_if_t<std::is_integral<S>::value, S>
    void bar(S&& s);
    template <class S, class = std::enable_if_t<!std::is_integral<S>::value, S>
    void bar(S&& s);
}

如果我们像上面那样声明它,那么我们如何定义它们呢?他们的两个函数签名看起来像:

template <class S, class>
inline void foo::bar(S&& s){ ... do something ... }

我见过一个返回 std::enable_if_t&lt;...&gt; 的示例,例如:

template <class S, class>
auto bar(S&& s) -> std::enable_if_t<!std::is_integral<S>::value, S>(...){
    ... do something ...
}

根据返回类型来消除歧义。但我不想退货。

【问题讨论】:

  • 看起来您实际上并没有使用 C++11。如果您实际上使用的是 C++17,我们可以使用 constexpr if 语句让事情变得更简单

标签: c++ c++11 templates sfinae enable-if


【解决方案1】:

因为默认参数不是函数签名的一部分,所以让它们不是默认的

class foo{
    template <class S, typename std::enable_if<std::is_integral<S>::value, int>::type = 0>
    void bar(S&& s);
    template <class S, typename std::enable_if<!std::is_integral<S>::value, int>::type = 0>
    void bar(S&& s);
};

Live Demo


编辑:根据大众的需求,这是 C++17 中的相同代码:

class foo{
public:
    template <class S>
    void bar(S&& s)
    {
        if constexpr(std::is_integral_v<S>)
            std::cout << "is integral\n";
        else
            std::cout << "NOT integral\n";
    }
};

constexpr if 语句对编译器来说是特殊的,因为分支是在编译时选择的,而未采用的分支甚至没有实例化

C++17 Demo

【讨论】:

  • 啊,我还需要在定义中添加相同的typename std::enable_if&lt;...&gt;,明白了。
  • 是的,要点不是使用class = 语法,而是使用显式类型。最好是一个整数,以便您可以默认其值(就像我们对 int0 所做的那样)
  • 我的方法是typename&lt;class S, std::enable_if_t&lt;condition&gt;* = nullptr&gt;
  • @milleniumbug:这是否是已定义的行为还有待商榷(void* 不是指向类类型的指针)让我找到问题页面。
  • 我明白了 - 那么为什么我们需要设置 = 0 或 = nullptr?我见过你不需要这样做的用法。另外,您可以添加 constexpr 答案吗?我很想了解它。在这种情况下,我使用的是 c++11,但仍然如此。我已经投了赞成票,并会接受你的回答,谢谢
【解决方案2】:

您仍然可以在返回类型中执行此操作。只需保留enable_if 的默认值(即void)。即使您只是使用 C++11,也只需添加此别名:

template <bool B, typename T=void>
using enable_if_t = typename std::enable_if<B, T>::type;

然后你可以这样做:

template <class S>
enable_if_t<std::is_integral<S>::value>
bar(S);

template <class S>
enable_if_t<!std::is_integral<S>::value>
bar(S);

或者:

template <class S>
auto bar(S) -> enable_if_t<std::is_integral<S>::value>

template <class S>
auto bar(S) -> enable_if_t<!std::is_integral<S>::value>

无论哪种方式,您都有两个正确消除歧义的函数,它们返回 void

【讨论】:

  • 重新定义已经将T = void 作为默认参数的std::enable_if_t 有什么意义?您的代码可以使用 std::enable_if_t 编译(使用 C++14)。也许你想写template &lt;bool B, typename T = void&gt; using enable_if_t = typename std::enable_if&lt;B, T&gt;::type; 使其成为 C++11?
【解决方案3】:

对于 C++11 编译器,另一个选项是使用标签调度。

template <class S>
void bar(S&& s)
{
    bar(std::forward<S>(s), std::is_integral<S>{});
}

template <class S>
void bar(S&& s, std::true_type)
{
    ... 
}

template <class S>
void bar(S&& s, std::false_type)
{
    ... 
}

【讨论】:

  • 啊,谢谢,我还没有熟悉 constexpr。由于我使用的是 c++11,我将不得不接受安迪的回答,但我赞成你的回答
  • @OneRaynyDay,Andy 更新了他的答案,所以我删除了constexpr 解决方案并为 C++11 添加了另一个解决方案。
  • @Evgeny: Love 标记为 dispatch +1。您可以直接将 std::is_integral{} 传递给 bar,而无需使用 integral_constant Demo
  • @AndyG,非常好的评论,谢谢。 std::is_integral 继承自 std::integral_constant。更正了答案。
  • @Evgeny:没错。可能的对象切片?确实!我们在乎吗?见鬼!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 2012-04-11
  • 1970-01-01
相关资源
最近更新 更多