【问题标题】:How am I supposed to use SFINAE overload resolution here?我应该如何在这里使用 SFINAE 重载解析?
【发布时间】:2021-07-27 08:51:08
【问题描述】:

我猜任何对 SFINAE 的使用都可能被认为是一种 hack,但在这里我尝试了很长时间,我能做到的最好的方法是在其中一个重载中使用默认的 void* 参数:

struct Dog 
{
    Dog() {}
    void makeNull() {}
    
};

// If no .makeNull() function this is eliminated
template <typename T>
constexpr auto HasMakeNullFunction() -> decltype(std::declval<T>().makeNull(), bool())
{
    return true;
}
// And this one is called. But I could only manage to do it with a default void* p = nullptr
template <typename T>
constexpr bool HasMakeNullFunction(void* p = nullptr)
{
    return false;
}

int main()
{

    constexpr bool b = HasMakeNullFunction<Dog>(); // True
    constexpr bool b2 = HasMakeNullFunction<int>(); // False

}

你应该怎么做?这确实有效,但是使用 SFINAE 的典型方法是使用一个在替换失败时调用的专用版本,对吧?另外,我不喜欢使用默认的 void*,因为我可能会看到误用和隐式转换为 void* 的可能性。

【问题讨论】:

  • 你更喜欢哪个版本的 C++?
  • @PatrickRoberts C++17。或者如果 C++20 有更好的解决方案,我也想看看。
  • 我不敢说这是 stackoverflow.com/q/257288/1896169 的副本,但这个问题应该有足够的信息来实现 HasMakeNullFunction
  • 这确实有效它真的不应该。当使用Dog 调用它时,nothings 会阻止 nullptr 版本有效,因此调用是ambiguous
  • @super 我之所以能这么做,是因为它更喜欢非默认参数版本。没有给出参数,所以首选应该是没有参数的函数。

标签: c++ templates sfinae


【解决方案1】:

C++20 concepts 之前,通常定义一个struct,它有条件地从std::true_typestd::false_type&lt;type_traits&gt; 中派生:

#include <type_traits>

template <class, class = void>
struct HasMakeNullFunction : std::false_type {};

template <class T>
struct HasMakeNullFunction<T, std::void_t<decltype(std::declval<T>().makeNull())>>
    : std::true_type {};

struct Dog 
{
    Dog() {}
    void makeNull() {}
};

int main()
{
    constexpr bool b = HasMakeNullFunction<Dog>::value; // true
    constexpr bool b2 = HasMakeNullFunction<int>::value; // false

    static_assert(b);
    static_assert(!b2);
}

godbolt.org

但是,有了概念,就更容易了:

template <class T>
concept HasMakeNullFunction = requires (T v) {
    { v.makeNull() };
};

struct Dog 
{
    Dog() {}
    void makeNull() {}
};

int main()
{
    constexpr bool b = HasMakeNullFunction<Dog>; // true
    constexpr bool b2 = HasMakeNullFunction<int>; // false

    static_assert(b);
    static_assert(!b2);
}

godbolt.org

【讨论】:

  • struct HasMakeNullFunction&lt;T, decltype(std::declval&lt;T&gt;().makeNull())&gt; 应该类似于 std::void_t&lt;decltype(...)&gt;
  • @Justin 真的没关系。这只是惯例,但没有必要让它发挥作用。
  • 如果没有std::void_t,您需要makeNull() 返回void。使用std::void_t,您允许其他返回类型。
  • @Justin 很好,我已经编辑了我的答案。
  • 你不需要std::void_t,你可以说:decltype(whatever(), void())
【解决方案2】:

您的代码 doesn't work 因为在将 Dog 指定为模板参数时,对 HasMakeNullFunction 的调用不明确。

您可以定义一个类型特征来完全分离这两个重载。例如

template <typename T, typename = void>
struct has_makeNull : std::false_type {};
template <typename T>
struct has_makeNull<T, decltype(std::declval<T>().makeNull(), void())> : std::true_type {};

template <typename T>
constexpr auto HasMakeNullFunction() -> std::enable_if_t<has_makeNull<T>::value, bool>
{
    return true;
}
template <typename T>
constexpr auto HasMakeNullFunction() -> std::enable_if_t<!has_makeNull<T>::value, bool>
{
    return false;
}

LIVE

【讨论】:

  • 在这种情况下,HasMakeNullFunctionhas_makeNull 是多余的
  • @Justin 你的意思是在没有has_makeNull 的情况下超载HasMakeNullFunction?但是如何将没有makeNull 的约束放在第二次重载HasMakeNullFunction 上?
  • 对不起,我的意思是用户可以不使用HasMakeNullFunction&lt;T&gt;(),而是直接使用has_makeNull&lt;T&gt;::value
  • @贾斯汀我明白了。我想 OP 想通过 SFINAE 控制重载解决方案,HasMakeNullFunction 可以作为它的示例,在has_makeNull 的帮助下。
  • @Zebrafish 我猜这是 VS 的问题;尤其是clanggcc 都说不。 :)
猜你喜欢
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多