充分利用 static_assert 和 std::is_invocable
Getting the best of static_assert and std::is_invocable
我有一个包含多个函数对象的库,当条件失败时,这些函数对象可能只接受几种类型,具体取决于 std::is_integral
. I want std::is_invocable
到 return 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_invocable
是 std::true_type
不满足std::is_integral
.
我尝试了一些涉及 decltype(auto)
、if constexpr
和其他机制的技巧和变体,但无法获得 class 错误消息很好且 std::is_invocable
的地方在询问是否可以使用不正确的类型调用 function
时对应于预期的 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
^~~~~~~~
注释显示在错误消息中,有点像静态断言消息?
这其实也是Concepts的动机之一。使用 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
那是……我想好一点。
我有一个包含多个函数对象的库,当条件失败时,这些函数对象可能只接受几种类型,具体取决于 std::is_integral
. I want std::is_invocable
到 return 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_invocable
是 std::true_type
不满足std::is_integral
.
我尝试了一些涉及 decltype(auto)
、if constexpr
和其他机制的技巧和变体,但无法获得 class 错误消息很好且 std::is_invocable
的地方在询问是否可以使用不正确的类型调用 function
时对应于预期的 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
^~~~~~~~
注释显示在错误消息中,有点像静态断言消息?
这其实也是Concepts的动机之一。使用 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
那是……我想好一点。