充分利用static_assert和std :: is_invocable

Mor*_*enn 10 c++ static-assert sfinae c++17

我有一个包含多个函数对象的库,可能只接受几种类型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 */ }
};
Run Code Online (Sandbox Code Playgroud)

有了这样的实现,std::is_invocablestd::false_type在未满足条件SFINAE如预期,但用户遇到丑陋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 */
    }
};
Run Code Online (Sandbox Code Playgroud)

有了这个实施,得到用户当原始SFINAE条件不满足,但友好的错误消息std::is_invocablestd::true_type,当问是否function实例可以处理类型不满足std::is_integral.

我试了几个技巧和涉及的变化decltype(auto),if constexpr和其他机制,但无法得到的一类,其中的错误消息是好的,其中std::is_invocable对应于预期的std::false_type询问时,是否function可以用不正确类型的调用.

我在这里错过了什么?有没有办法获得正确std::is_invocable 用户友好的错误消息?

Bar*_*rry 7

这是一个糟糕的方式.添加过载:

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
Run Code Online (Sandbox Code Playgroud)

这满足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
          ^~~~~~~~
Run Code Online (Sandbox Code Playgroud)

注释显示在错误消息中,有点像静态断言消息?


这实际上是Concepts的动机之一.用a requires而不是a 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
Run Code Online (Sandbox Code Playgroud)

那是......我猜的好一点.

  • 我发现[一篇文章](https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html)改进了`= delete`技巧,允许提供专用的`static_assert `消息,这不错. (4认同)