防止在std::函数中使用不同类型包装lambda

Prevent wrapping lambda in std::function with different types

本文关键字:同类型 lambda 包装 std 函数      更新时间:2023-10-16

我有一个类,它的构造函数是std::function<void(T)>。如果我声明T为double,但向构造函数传递一个带int的lambda,编译器会让我在没有警告的情况下离开。(更新:MSVC发出警告,clang没有)。有可能修改这段代码,使我得到一个编译器错误(w/clang)吗?

我很惊讶这个工作:

#include <functional>
#include <iostream>
template <typename T>
class Thing {
public:
  using Handler = std::function<void(T)>;
  Thing(Handler handler)
  : handler_(handler)
  {}
  Handler handler_;
};
int main() {
  Thing<double> foo([](int bar) {  // Conversion to void(int) from void(double)
    std::cout << bar;
  });
  foo.handler_(4.2);
  return 0;
}

并在没有警告的情况下编译:

$ clang --std=c++11 -lc++ -Wall -Werror test.cc
$ ./a.out
4

这样的转换似乎会导致不必要的副作用。我无法想象在任何情况下,这种行为都会是人们想要的。

您可以使用一个模板类作为参数,该模板类只允许转换为T:

template <typename T>
struct Strict {
    // This is a bit simplistic, but enough for fundamental types
    Strict(T t_) : t(t_) {}
    operator T() const { return t; }
    template<typename U>
    operator U() const = delete;
    T t;
};
template <typename T>
class Thing {
public:
    using Handler = std::function<void(Strict<T>)>;
    //...
};

演示

然而,请注意,使用这种方法,扩大转换不会起到很好的作用:

// doesn't compile
Thing<int> foo{[](long long bar){});

对于未捕获的lambda,您可以依赖于它们衰减为指向函数的指针这一事实
使用此行:

using Handler = void(*)(T);

而不是CCD_ 3的当前定义
这样,除非将参数类型更改为所需类型,否则您将收到错误。

换句话说,这(不)如预期的那样工作:

#include <functional>
#include <iostream>
  template <typename T>
  class Thing {
  public:
    using Handler = void(*)(T);
    Thing(Handler handler)
    : handler_(handler)
    {}
    Handler handler_;
};
int main() {
    // switch the type of bar to double and it will compile
    Thing<double> foo([](int bar) {
        std::cout << bar;
    });
    foo.handler_(4.2);
    return 0;
}

请注意,如果您有Lambda的捕获列表,则此解决方案将不再有效
无论如何,它解决了问题中所解释的问题。