我应该如何在这里使用 SFINAE 重载解析?
How am I supposed to use SFINAE overload resolution here?
我想任何对 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* 的可能性。
您的代码 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;
}
在C++20 concepts, it was typical to define a struct
that conditionally derived from either std::true_type
or std::false_type
in <type_traits>
之前:
#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);
}
不过,有了概念,就更简单了:
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);
}
我想任何对 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* 的可能性。
您的代码 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;
}
在C++20 concepts, it was typical to define a struct
that conditionally derived from either std::true_type
or std::false_type
in <type_traits>
之前:
#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);
}
不过,有了概念,就更简单了:
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);
}