概念检查函数不适用于仅可移动参数

Concept checking a function doesn't work with movable-only arguments

本文关键字:可移动 参数 适用于 不适用 检查 函数      更新时间:2023-10-16

我有一个函数,它调用一个回调函数,该函数接受仅可移动类型(例如unique_ptr(。

template <typename Function>
void foo(const Function& function) {
    BOOST_CONCEPT_ASSERT((
            boost::UnaryFunction<Function, void, std::unique_ptr<Bar>));
    auto bar = std::make_unique<Bar>();
    ...
    function(std::move(bar));
}

在试图编译此代码时,我收到一条消息,BOOST_CONCEPT_ASSERT行试图复制unique_ptr。如果我去掉这行,代码就可以正常工作。似乎是助推。概念库不支持移动语义。如果不编写我自己的概念类(顺便说一句,支持左值和右值作为它们的参数并不是很简单(,有什么解决方法吗。

这是正确的。不幸的是,UnaryFunction作为一个概念被写成:
  BOOST_concept(UnaryFunction,(Func)(Return)(Arg))
  {
      BOOST_CONCEPT_USAGE(UnaryFunction) { test(is_void<Return>()); }
   private:
      void test(boost::mpl::false_)
      {    
          f(arg);               // "priming the pump" this way keeps msvc6 happy (ICE)
          Return r = f(arg);
          ignore_unused_variable_warning(r);
      }    
      void test(boost::mpl::true_)
      {    
          f(arg); // <== would have to have std::move(arg)
                  // here to work, or at least some kind of
                  // check against copy-constructibility, etc.
      }    
#if (BOOST_WORKAROUND(__GNUC__, BOOST_TESTED_AT(4) 
                      && BOOST_WORKAROUND(__GNUC__, > 3)))
      // Declare a dummy construktor to make gcc happy.
      // It seems the compiler can not generate a sensible constructor when this is instantiated with a refence type.
      // (warning: non-static reference "const double& boost::UnaryFunction<YourClassHere>::arg"
      // in class without a constructor [-Wuninitialized])
      UnaryFunction();
#endif
      Func f;
      Arg arg; 
  };

由于arg是通过左值传递的,因此没有办法让它与Boost一起工作。概念。直接地你可以写一个破解。由于我们只是调用检查f(arg)是否有效,因此我们可以为arg构造一个可转换为unique_ptr<Bar>的本地类型。即:

template <typename Function>
void foo(Function f)
{
    struct Foo {
        operator std::unique_ptr<int>();
    };
    BOOST_CONCEPT_ASSERT((
            boost::UnaryFunction<Function, void, Foo>));
    f(std::make_unique<int>(42));
}

或者更一般地说:

template <typename T>
struct AsRvalue {
    operator T(); // no definition necessary
};
template <typename Function>
void foo(Function f)
{
    BOOST_CONCEPT_ASSERT((
            boost::UnaryFunction<Function, void, AsRvalue<std::unique_ptr<int>>>));
    f(std::make_unique<int>(42));
}

这为我编译了gcc和clang(尽管在clang上给出了关于未使用的typedef的警告(。然而,在这一点上,写下你自己的概念以使其发挥作用可能会更清楚。像Piotr这样的东西最简单。

#include <type_traits>
#include <utility>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename, typename = void_t<>>
struct is_callable : std::false_type {};
template <typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};
//...
static_assert(is_callable<Function&(std::unique_ptr<Bar>)>{}, "Not callable");

DEMO