【问题标题】:How to compare types (including ref-qualifiers) in a parameter pack and the types of std::function parameters如何比较参数包中的类型(包括引用限定符)和 std::function 参数的类型
【发布时间】:2021-02-19 17:04:58
【问题描述】:

我将std::function 存储在可变参数类模板中(在类的构造函数中传递)。

这样做时,我想检查std::function 参数的类型是否与类模板的参数包中的类型相同。这是一个例子:

template<typename... T>
class Foo {
public:
    explicit Foo(std::function<void(T...)> f)
        : func(std::move(f)) {
        // static_assert(...) How to formulate the static_assert here?
    }

    std::function<void(T...)> func;    
};

int main()
{
    Foo<int> fooA([](int& a){}); // does not compile 
    Foo<int&> fooB([](int a){}); // should not compile, but does
    
    Foo<int&> fooC([](int& a){}); // should compile 
}

如果类Foo&lt;int&amp;&gt; 是用引用类型定义的,我想确保 lambda 也通过引用而不是值来获取int

反过来(Foo&lt;int&gt; 和带有 int&amp; 的 lambda)已经无法编译,因为 lambda 无法转换为按值采用 int 的函数。

使用static_assert(),我试图确保lambda 中的参数类型与Foo 中作为模板参数给出的类型相匹配。

到目前为止,我已经尝试解开函数参数:

template<typename... T>
struct ID {};

template<typename Func>
struct Unwrap;

template<typename R, typename... Args>
struct Unwrap<R(Args...)> {
    using ArgsType = ID<Args...>;
};

template<typename R, typename... Args>
struct Unwrap<std::function<R(Args...)>> 
    : Unwrap<R(Args...)> {};

...
static_assert(std::is_same<typename Unwrap<std::function<void(T...)>>::ArgsType, ID<T...>>::value, "");
...

有没有办法实现我想要的? (对我来说考虑这样的检查是否有意义,或者使用我的class Foo 的人是否应该确保他提供了具有正确参数类型的 lambda?)

【问题讨论】:

    标签: c++ variadic-templates std-function template-argument-deduction


    【解决方案1】:

    如果你可以使用C++17...那么使用为std::function 定义的CTAD 来检查为构造函数的参数推导的std::function 是否与func 完全相同?

    我的意思是……下面呢?

    template <typename... T>
    struct Foo
     {
       template <typename L>
          Foo (L && l) : func{ std::forward<L>(l) }
        {
          using T1 = decltype(func);
          using T2 = decltype(std::function{std::forward<L>(l)});
    
          static_assert( std::is_same_v<T1, T2>, "a better failure message, please" );
        }
    
       std::function<void(T...)> func;    
     };
    

    使用这个Foo 你会得到

    //Foo<int> fooA([](int& a){}); // compilation error (assigning func) 
    //Foo<int&> fooB([](int a){}); // compilation error (static_assert failure)
    
    Foo<int&> fooC([](int& a){}); // compile 
    

    --- 编辑---

    OP 观察到

    Foo&lt;void(int&amp;)&gt; fooD([](auto&amp;) {}); 不会编译

    不幸的是,此解决方案强加了std::function 参数 CTAD 推导,因此当参数是泛型 lambda 时会出现编译错误。

    我们可以 SFINAE 停用 static_assert(),以防 std::function 不可推断

    constexpr std::false_type Bar (...);
    
    template <typename L>
    constexpr auto Bar (L && l)
       -> decltype( std::function{std::forward<L>(l)}, std::true_type{} );
    
    template <typename... T>
    struct Foo
     {
       template <typename L>
          Foo (L && l) : func{ std::forward<L>(l) }
        {
          if constexpr ( decltype(Bar(std::forward<L>(l)))::value == true )
           {
             using T1 = decltype(func);
             using T2 = decltype(std::function{std::forward<L>(l)});
    
             static_assert( std::is_same_v<T1, T2>, "a better message, please" );
           }
        }
    
       std::function<void(T...)> func;    
     };
    

    所以

    Foo<int &> fooF([](auto &){});
    

    变得可编译。

    当请求一个值时,继续给一个泛型 lambda 接收泛型引用报错

    //Foo<int>   fooD([](auto &){}); // compilation error (assigning func)
    

    因为分配func的错误仍然存​​在。

    问题是,停用测试,下面的代码编译

    Foo<int &> fooE([](auto){}); // compile (!)
    

    我不知道如何避免。

    【讨论】:

    • 感谢您的回复。我想指出的一件事是Foo&lt;void(int&amp;)&gt; fooD([](auto&amp;) {}); 无法编译,因为std::function 的模板参数无法推断。我认为,由于转发参考,这会丢失。
    • @maro213_ - 不幸的是,你是对的:这是此解决方案的一个限制,因为无法从std::functions CTAD 推导出通用 lambda。
    • @maro213_ - 我已经扩展了答案,以显示当 std::function 不可推断时如何禁用 static_assert。现在Foo&lt;void(int&amp;)&gt; fooD([](auto&amp;) {}); 编译。问题是Foo&lt;void(int&amp;)&gt; fooD([](auto) {});
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-15
    • 2021-07-31
    相关资源
    最近更新 更多