充分利用static_assert和标准::is_invocable
Getting the best of static_assert and std::is_invocable
我有一个包含多个函数对象的库,根据std::is_integral
,这些对象可能只接受几种类型。我希望std::is_invocable
在条件失败时返回false
,但当用户尝试调用函数对象的实例时,我也想要一条很好的static_assert
错误消息。以下是我目前拥有的函数对象的简化示例:
struct function
{
template<typename Iterator>
auto operator()(Iterator first, Iterator last) const
-> std::enable_if_t<std::is_integral_v<
typename std::iterator_traits<Iterator>::value_type
>>
{ /* something */ }
};
通过这样的实现,当不满足 SFINAE 条件时,std::is_invocable
按预期std::false_type
,但当用户尝试使用不符合 SFINAE 条件的参数调用函数对象时,会遇到丑陋的 SFINAE 错误消息。
为了获得更好的错误消息,我尝试了以下解决方案:
struct function
{
template<typename Iterator>
auto operator()(Iterator first, Iterator last) const
-> void
{
static_assert(std::is_integral_v<typename std::iterator_traits<Iterator>::value_type>,
"function can only be called with a collection of integers");
/* something */
}
};
通过此实现,当不满足原始 SFINAE 条件时,用户会收到友好的错误消息,但当询问function
实例是否可以处理不满足std::is_integral
的类型时,std::is_invocable
std::true_type
错误消息。
我尝试了几种涉及decltype(auto)
、if constexpr
和其他机制的技巧和变体,但无法获得一个错误消息很好的类,并且在询问是否可以使用不正确的类型调用function
时std::is_invocable
对应于预期std::false_type
。
我在这里错过了什么?有没有办法同时获得正确的std::is_invocable
和用户友好的错误消息?
这是一个可怕的方法。添加重载:
template <typename Iterator>
auto operator()(Iterator first, Iterator last) const
-> std::enable_if_t<std::is_integral_v<
typename std::iterator_traits<Iterator>::value_type
>>;
template <typename Iterator, class... Args>
void operator()(Iterator, Iterator, Args&&... ) const = delete; // must be integral
这满足is_invocable<>
条件,因为受约束的函数模板更专业且更首选 - 因此,如果满足条件,则函数是可调用的,否则将被删除。
这在错误情况下做得更好一些,因为如果您尝试调用它,您会得到:
foo.cxx: In function ‘int main()’:
foo.cxx:31:13: error: use of deleted function ‘void function::operator()(Iterator, Iterator, Args&& ...) const [with Iterator = std::__cxx11::basic_string<char>*; Args = {}]’
f(&i, &i);
^
foo.cxx:19:10: note: declared here
void operator()(Iterator, Iterator, Args&&... ) const = delete; // must be integral
^~~~~~~~
注释显示在错误消息中,这有点像静态断言消息?
这实际上是概念画板的动机之一。用requires
而不是enable_if
,我们得到:
foo.cxx: In function ‘int main()’:
foo.cxx:26:13: error: no match for call to ‘(function) (std::__cxx11::string*, std::__cxx11::string*)’
f(&i, &i);
^
foo.cxx:11:10: note: candidate: void function::operator()(Iterator, Iterator) const requires is_integral_v<typename std::iterator_traits<_Iter>::value_type> [with Iterator = std::__cxx11::basic_string<char>*]
void operator()(Iterator first, Iterator last) const
^~~~~~~~
foo.cxx:11:10: note: constraints not satisfied
foo.cxx:11:10: note: ‘is_integral_v<typename std::iterator_traits<_Iter>::value_type>’ evaluated to false
那是。。。我想好一点。
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- Why is UINT32_MAX + 1 = 0?
- C++错误:"error: int aaa::bbb is protected within this context"
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 铸造标准::有没有回到原来的类型
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 使用 cmake 的 Linux 终端上的"Conversion to non-scalar type is requested"错误
- 标准库类型的赋值运算符的引用限定符
- 标准是否严格定义了该程序应该如何编译?
- 如何从Windows应用程序输出到标准?
- 安全到标准:移动会员?
- 如何正确将字符串转换为标准::时间::system_clock::time_point?
- 这是否符合C++标准:双响双响,例如!!(-0.0).
- C++11标准是否保证"auto n2 = const_cast<int&>(n);" "n2 is int&"?