【问题标题】:Templated lambdas in C++17 without an auto argument没有自动参数的 C++17 中的模板化 lambda
【发布时间】:2018-07-19 16:38:32
【问题描述】:

我有一个类Derived,它继承自类Base<ResourceType>

template <class ResourceType>
class Base {
protected:
  ResourceType* resource;
public:
  void set_resource(ResourceType* resource) {
    this->resource = resource;
  }
};

template <class ResourceType>
class Derived : public Base<ResourceType> {
public:
  using Base<ResourceType>::resource;
  void print () {
    std::cout << *resource << std::endl;
  }
};

我想创建一个工厂来创建Derived 类型的对象。我当然可以用函数来做到这一点:

template <typename ResourceType>
auto derived_factory () { 
  return new Derived<ResourceType>(); 
}

auto derived = *(derived_factory<int>());

但是,我无法为工厂编写 lambda 函数。如果我使用 auto 关键字接受模板参数,我可以编写模板化的 lambda 函数,但在这里我只想使用模板来确定返回类型。以下失败:

auto derived_factory = []<typename ResourceType>() {
  return new Derived<ResourceType>();
};

auto derived = *(derived_factory<int>());

出现错误:

inherit_unknown_type.cpp: In function ‘int main()’:
inherit_unknown_type.cpp:27:36: error: expected primary-expression before ‘int’
   auto derived = *(derived_factory<int>());
                                    ^~~
inherit_unknown_type.cpp:27:36: error: expected ‘)’ before ‘int’

我只是错误地调用了 lambda 吗?还是要等C++20

【问题讨论】:

  • 我猜你可以使用一个幻像类型的虚拟参数,但你为什么首先想要一个 lambda?
  • 我猜与人们为什么更喜欢 lambda 而不是函数的原因相同。我们在其他地方使用了lambda,所以会和代码保持一致。
  • 我不知道为什么有人会不加选择地偏爱 lambdas。如果你给它一个名字并且没有捕获,它也可以是一个常规函数。
  • 我猜你说得有道理。为了给你更多的上下文,我们的库提供了一个类,它的对象使用用户提供的工厂函数创建用户定义类的对象。这些工厂函数仅用于提供给我们库类的构造函数。而且 C++ 不支持嵌套函数。对于用户来说,编写 lambdas 而不是函数可以说更整洁,因为他们可以在构造函数调用中内联 lambdas。用户编写他们不调用自己的函数,而只是传递给构造函数并不理想。
  • 好的,如果函数是用户的代码,那么没有理由不允许它成为 lambda。如果 C++20 不是一个选项,我会使用标签调度(我在上面的评论中将其称为幻像类型)。

标签: c++ templates lambda c++17 generic-lambda


【解决方案1】:

lambda 表达式中的模板参数列表是C++20 feature

(事实上,我的 GCC 在诊断中这么说:error: lambda templates are only available with -std=c++2a or -std=gnu++2a [-Wpedantic]

但您不必等待 C++20,它已经是 GCC 8 的 supported-std=c++2a 标志。

您必须更改调用语法:您需要derived_factory.operator()&lt;int&gt;(),而不是derived_factory&lt;int&gt;()


作为替代方案(如果您不想要免费功能),我建议使用标签调度的变体:

auto derived_factory = [](auto tag) {
    return new Derived<typename tag::type>();
};

template <typename T> struct tag_type {using type = T;};

// Usage:
derived_factory(tag_type<int>{})

另外,即使你以某种方式编译它,这一行:

auto derived = *(derived_factory<int>());

无论如何都会导致内存泄漏。为避免这种情况,您应该将结果存储为指针或引用。或者更好的是,使用智能指针。

【讨论】:

  • 你不需要声明tag_type。在 C++17 中,您可以使用 std::common_type,在 C++20 中甚至可以使用 std::type_identity
【解决方案2】:

等待 C++20,您可以从模板类返回 lambda

template <typename ResourceType>
auto make_derived_factory ()
 { return []{ return new Derived<ResourceType>{}; }; }

auto derived = make_derived_factory<int>();

int main ()
 {
   auto df { derived() };
 }

【讨论】:

    【解决方案3】:

    上述方法不起作用,但这样做:

    auto derived_factory = [](auto tag) 
    {
        return new Derived<decltype(tag)::type>();
    };
    
    template <typename T> struct tag_type {using type = T;};
    
    // Usage:
    derived_factory(tag_type<int>{})
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-27
      • 2018-10-12
      • 1970-01-01
      • 2018-07-17
      • 2013-06-15
      • 2019-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多