如何编写只能接受 int 或 float 指针的 lambda 函数?

How to write a lambda function that can only take in int or float pointers?

提问人:24n8 提问时间:6/2/2022 最后编辑:dfrib24n8 更新时间:6/4/2022 访问量:223

问:

我需要创建一个 lambda 函数,该函数可以接受 a 或 a ,例如,类似int *float *

auto func = [](void* ptr) {
  // do some assignments with the pointer
}

目前,我正在使用这种方法,但我想知道还有哪些其他方法可以实现我想要的东西?void *

我无法访问代码库中的 C++17 功能,因此必须坚持使用 C++14 或更低的语义。

C++ 指针 lambda C++14

评论

0赞 Mad Physicist 6/2/2022
可能是某种模板。
0赞 24n8 6/2/2022
@MadPhysicist 那不是只在 C++20 中可用吗?
0赞 Mad Physicist 6/2/2022
键入“何时将模板添加到 c++ 到 google 中”。回答是“1991 年 10 月,在该语言的 3.0 版中引入了模板”。我认为你可以使用模板。
0赞 24n8 6/2/2022
@MadPhysicist 在这种情况下,我说的是模板化 lambda
0赞 davidhigh 6/2/2022
@MadPhysicist:哈哈,“可能是某种模板”基本上是所有C++问题的答案

答:

6赞 dfrib 6/2/2022 #1

C++14 具有通用 lambda,这意味着您可以在它们上使用 SFINAE,例如在它们的尾随返回类型中:

#include <type_traits>

template <typename T> struct valid_ptr_type : std::false_type {};
template <> struct valid_ptr_type<int *>    : std::true_type {};
template <> struct valid_ptr_type<float *>  : std::true_type {};

template <typename T> constexpr bool valid_ptr_type_v{valid_ptr_type<T>::value};

int main() {
  // only allow ptr args that fulfills
  // the valid_ptr_type_v<decltype(ptr)> trait
  auto func = [](auto *ptr) ->
      std::enable_if_t<valid_ptr_type_v<decltype(ptr)>> {
          // do stuff with ptr
      };  // note: void return type (std::enable_if default)

  int a{};
  float b{};
  char c{};
  func(&a);
  func(&b);
  // func(&c);  // error
}

这在后台的作用是约束 lambda 闭包类型的模板函数调用运算符的推导的单个模板参数。

// ~ish
struct AnonClosureTypeOfGenericLambda {
    template <typename T, 
              typename = std::enable_if_t<valid_ptr_type_v<decltype(T*)>>>
    void operator()(T* ptr) const { /* ... */ }
};

评论

0赞 Remy Lebeau 6/3/2022
请注意,C++14(在OP的要求范围内)确实有,所以你不需要使用std::enable_if_ttypename std::enable_if<...>::type
0赞 24n8 6/3/2022
应该是template <> struct valid_ptr_type<char *> : std::true_type {};template <> struct valid_ptr_type<float *> : std::true_type {};
0赞 dfrib 6/3/2022
@RemyLebeau啊,是的,我总是将 C++17 的可变模板助手的引入与 C++14 的别名模板助手混为一谈。已修复,谢谢。_v::value_t::type
0赞 dfrib 6/3/2022
@24n8啊,是的,你要求的只是,而不是.固定。float*int*char*
2赞 doug 6/2/2022 #2

我建议使用具有清晰的代码和编译错误。此外,虽然不是问题,但由于这两种类型都是 ptrs to float 或 int,您可以使用条件根据实际类型进行不同的计算。static_assert

#include <type_traits>
int main()
{
    auto foo = [](auto p) {
        static_assert(std::is_same_v<decltype(p), float*> || std::is_same_v<decltype(p), int*>);
        if (std::is_same_v<decltype(p), int*>)
            *p = 42;
         else
            *p = 42.1f;
    };
    float f{};
    int i{};
    double d{};

    // compiles
    foo(&f);
    foo(&i);
    // doesn't compile
    //foo(&d);
}
5赞 davidhigh 6/2/2022 #3

通过使用 lambda 重载技巧:

template<typename... Ts> struct overload : public Ts... { using Ts::operator()...; };
template<typename... Ts> overload(Ts...)->overload<Ts...>;

auto f = overload([](int *) { /* do something with int-pointer */ }
                , [](float *){ /* do something with void-pointer */ });

但那是 C++17。以下是 C++ 11 解决方案:

template <class... Fs>
struct overload_t;

template <typename F, typename ... Fs>
struct overload_t<F, Fs...> : F, overload_t<Fs...>
{
    overload_t(F f, Fs... fs) : F(std::move(f)), overload_t<Fs...>(std::move(fs)...) {}

    using F::operator();
    using overload_t<Fs...>::operator();
};

template <typename F>
struct overload_t<F> : F
{
    overload_t(F f) : F(std::move(f)) {}

    using F::operator();
};

template <typename... Fs>
overload_t<Fs...> overload(Fs... fs)
{
    return overload_t<Fs...>(std::move(fs)...);
}

评论

0赞 dfrib 6/2/2022
这利用了 CTAD,因此达不到 OP 的 C++14 要求。
1赞 davidhigh 6/2/2022
@dfrib:jup,我自己刚刚认识到了这一点,并添加了一个 C++11 解决方案。