c++ - 如何为 C++ 重载解析和 SFINAE 编写正/负测试?
问题描述
我目前正在设计一些利用 SFINAE 来控制重载分辨率的函数。这很容易出错,所以我希望能够为可以调用 API 的内容编写正面和负面的测试。例如:
// A templated function that should only be used via type deduction. (We
// don't want to let users to set template arguments explicitly in order
// to reserve the right to change them. They are not a public API.)
template <
int&... ExplicitArgumentBarrier,
typename T,
typename = std::enable_if<!std::is_same_v<T, int>>>
void AcceptVector(std::vector<T>);
// We should be able to feed the function most vectors.
static_assert(kCanCallAcceptVector<std::vector<double>>);
static_assert(kCanCallAcceptVector<std::vector<std::string>>);
// But not vector<int>.
static_assert(!kCanCallAcceptVector<std::vector<int>>);
感觉我应该可以用std::is_invocable_v
. 相反,我真的想测试重载解决方案:无论有多少重载以及它们的模板参数是什么样的,提供特定类型作为参数AcceptVector
的调用站点是否有效?AcceptVector
最好的方法是什么?这是我想出的:
#define DEFINE_CAN_CALL_VARIABLE(namespace_name, function_name) \
template <typename... Args> \
struct internal_CanCall##function_name final { \
private: \
struct Functor { \
template <typename... U, \
typename = decltype(::namespace_name ::function_name( \
std::declval<U>()...))> \
void operator()(U&&... args); \
}; \
\
public: \
static constexpr bool kValue = std::is_invocable_v<Functor, Args...>; \
}; \
\
template <typename... Args> \
inline constexpr bool kCanCall##function_name = \
internal_CanCall##function_name<Args...>::kValue;
DEFINE_CAN_CALL_VARIABLE(my::project, AcceptVector);
这个想法是定义一个我们可以命名的函子,std::is_invocable_v
然后依靠该函子中的重载决议。但也有一些缺点:
我必须将它打包成一个宏,以使其可重用,而不会
std::is_invocable_v
再次出现同样的问题。我需要让用户将命名空间作为宏参数提供,以避免因参数相关查找引起的问题。(虽然我说我对任何 call spelled 感兴趣
AcceptVector
,但我想我的意思是任何AcceptVector
可以解析为AcceptVector
我项目命名空间中的函数的 call spelled 。)我不是 100% 确定我没有错过其他边缘情况。
这似乎是正确的吗?有没有更糟糕的方法来做到这一点?
解决方案
由于您标记了此 C++20,因此更好的方法是编写一个概念:
template <typename... Args>
concept canAcceptVector = requires (Args(*args)()...) {
AcceptVector(args()...);
};
然后您可以测试:
// We should be able to feed the function most vectors.
static_assert(canAcceptVector<std::vector<double>>);
static_assert(canAcceptVector<std::vector<std::string>>);
// But not vector<int>.
static_assert(!canAcceptVector<std::vector<int>>);
函数指针的奇怪公式是确保canAcceptVector<int>
、canAcceptVector<int&>
和canAcceptVector<int&&>
尝试AcceptVector
分别使用纯右值、左值和 xvalue 调用。在这种情况下可能不是超级重要,但我发现它比处理std::forward
和正确处理prvalues稍微不那么笨拙。
对于这个问题,最好的 C++17 方法是检测习语,它需要额外的概念:
template <typename... Args>
using AcceptVector_t = decltype(AcceptVector(std::declval<Args>()...));
template <typename... Args>
inline constexpr bool canAcceptVector = is_detected_v<AcceptVector_t, Args...>;
您可以static_assert
以与上述相同的方式。
推荐阅读
- regex - 如何使用 awk 检查行上的第一个模式?
- selenium-webdriver - selenium UI 自动化是否真的可以在中型到大型项目中甚至不使用单个 sleep() 的情况下实现?
- laravel - Laravel 图像响应头 access-control-allow-origin: * 设置问题
- python - 试图建立一个预测模型来处理缺失值和编码分类特征
- google-bigquery - Google Ads 的大查询数据传输
- python - 在 python jupyter 中导入模块问题
- laravel - 在laravel livewire中使用choice.js进行选择/多选不起作用
- ubuntu-20.04 - wps office 在 kso_qt::QSocketNotifier::type() 中因 SIGSEGV 崩溃
- zeal - 如何以热情或速度使用 mozila 开发者网络文档?
- xcode10 - 有什么方法可以使用 iOS 15 在高山脉上运行 xcode 10