【问题标题】:How to convert a constructor to a std::function [duplicate]如何将构造函数转换为 std::function [重复]
【发布时间】:2019-09-25 04:49:44
【问题描述】:

有没有办法将构造函数分配给像'f'这样的函数:

struct C
{
    C(int){};
};

auto f = C::C(int); // not work
auto g = [](int){ return C(int()); }; // works

C、C::C、&C、&C::C、C::&C、C::C(int)、&C::C(int) ... 都不起作用

Frid​​ay Pie 提出的解决方案:

template <class T>
struct ConstructHelper
{
    template <class... Args>
    static T&& construct(Args&&... args)
    {
        return std::move(T(std::forward<Args>(args)...));
    }
};

auto f = ConstructHelper<C>::construct<int>; // works
auto c = apply(f, 0); // works

【问题讨论】:

  • 你试图做的事情没有意义。
  • @Eugene 类似于 apply(func, args...),我们应该在 c++ 中有 apply(cls, args...)
  • 你要allocator_traits&lt;C, Args...&gt;::construct吗?但是接下来:谁提供构造C 的内存位置?这就是它没有意义的原因。
  • 构造函数没有类型也没有地址,并且不能在函数调用表达式中显式调用。它不会返回任何东西,甚至不会返回 void

标签: c++


【解决方案1】:

您尝试编写的代码遵循除您编写的 lambda 之外的其他规则。

在第一个中,您试图获取指向成员函数的指针:&amp;C::C 这将类似于&amp;C::print(假设参数为 int)此函数的第一个参数将是指向 C 的指针,即其次是int。

但是,我们正在尝试为构造函数执行此操作。所以我们没有有效的 C 来处理。结果就是这根本无法编译。

请注意,如果您确实想在现有内存上执行构造函数,则需要放置 new。

您的第二个代码是 lambda。简而言之:一个实现了operator() 的类。在此运算符中,您可以像在任何其他函数中一样编写代码,并按值返回一个新构造的实例。 (虽然,这看起来很像一个令人烦恼的解析)这类似于执行之前提到的打印函数。

所以两者都有不同的语义,你的编译器没有编译这段代码是正确的。

【讨论】:

    【解决方案2】:

    理论上,您的假设函数 apply 看起来像模板一样简单

    #include <iostream>
    #include <type_traits>
    struct C
    {
        C(int){};
    };
    
    template <class F, class... Args>
    auto apply ( F&& f, Args&&... args) -> typename std::result_of<F(Args...)>::type
    {
        return f(args...);
    }
    
    float foo (int a, float b) {  return a*b; }
    
    int main()
    {
        auto b = apply(&foo, 3, 5.f);
        //auto b = apply(&C::C, 3);  //error: taking address of constructor 'C::C'
    
        return 0;
    }
    

    但是按照 ISO 标准,不可能获取构造函数的地址。在大多数情况下,这个特殊函数在结果代码中是一个非实体。

    不允许显式调用构造函数。你的 lambda 函数做不同的事情。它根据该表达式的定义效果构造C 类的对象。

    您可以使用虚假参数和 SFINAE

     template <class F, class... Args>
        auto apply ( F&& f, Args&&... args) 
            -> std::enable_if_t<std::is_invocable<F, Args...>::value,
               typename std::result_of<F(Args...)>::type>
    {
        return f(args...);
    }
    
    template <class T, class... Args>
    auto apply ( T&&, Args&&... args)
        -> std::enable_if_t<!std::is_invocable<T, Args...>::value,T&&>
    {
        return std::move(T(args...));
    }
    

    但最好避免这种情况并重新考虑您想到的任何元编程模式,即避免尝试为不同类型的参数调用相同的模板名称,或使用 lambda 进行修正。例如。您可以避免使用 SFINAE 致电apply()

    【讨论】:

    • 那么没有办法在 C++11 中进行apply 调用来构造非默认可构造和不可复制构造的类?
    • 即C(1, 2, 3) vs apply(some-constructor-func, 1, 2, 3) 其中 C 是不可默认构造和不可复制构造的
    • @MidoriYakumo 应该能够创建可移动的。而且通常非默认、不可复制的类是非常特殊的。像单身人士或独特的权威持有者(例如 unique_ptrboost::scoped_ptr
    • @MidoriYakumo 你的 C 是可复制的。虽然它不是可移动的。为了不可复制,它必须有:C(const C&amp;) = delete;C(C&amp;&amp; o) 明确声明。它应该是可移动的或可复制的,否则上面的分配将不起作用。
    猜你喜欢
    • 2021-08-16
    • 2018-11-06
    • 1970-01-01
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多