【问题标题】:Lambda type deductionLambda 类型推导
【发布时间】:2018-08-29 00:14:30
【问题描述】:
auto dothings = [](long position) {
auto variable;
/*do things*/
return variable;
};

float x = dothings(1l);
char  y = dothings(2l);

基本上,我很好奇的是,lambda 中的变量是否有可能以任何方式推断返回值分配给的类型,在这种情况下,它是 float 和 char。是否有等效于模板类型名?谢谢。

【问题讨论】:

  • 它会为你编译吗?
  • 这不是重复的,他基本上是在问泛型函数是否可以推断返回类型
  • 不,表达式的左侧不参与右侧的类型推导(模板化转换运算符除外)。
  • lambda 不能使用右侧,因为即使 lambda 保持不变,右侧的类型也会发生变化——但这需要它推断出不同的类型。

标签: c++ lambda return-value type-deduction


【解决方案1】:

这可以做到,但它 a) 有点复杂,b) 在 99.9% 的情况下,这并不是一个好主意。这是你如何进行的。您可以根据分配表达式的类型执行某些操作的唯一方法是利用隐式转换。这需要一个模板化的隐式对话运算符,它不能在 lambda 中本地声明,所以我们必须从编写一些支持代码开始:

template <class T>
struct identity {
    T get(); // not defined
};


template <class F>
struct ReturnConverter {

    F f;

    template <class T>
    operator T() {
        return f(identity<T>{});
    }
};

template <class F>
auto makeReturnConverter(F f) { return ReturnConverter<F>{f}; }

第一个类只是帮助 lambda 推断类型。第二个类是一个本身接受 lambda(或任何可调用)的类,并且具有到任何类型的隐式转换运算符。当请求转换时,它调用可调用对象,使用我们的identity 类模板作为输入类型的一种方式。我们现在可以像这样使用它:

auto doIt = [] (long l) {
    return makeReturnConverter([=] (auto t) {
        return l + sizeof(decltype(t.get()));
    });
};

这个 lambda 通过输入另一个 lambda 来创建我们特殊的 ReturnConverter 类。这个 lambda 捕获外部 lambda 的 long l 参数(按值),它准备接受我们的特殊身份类作为唯一参数。然后它可以退出“目标”或目标类型。我在这里使用sizeof 来快速展示一个示例,其中结果取决于 lambda 的参数和目标类型。但请注意,一旦我使用decltype(t.get()) 获得类型,我实际上可以声明该类型的变量并用它做任何我想做的事情。

float x = doIt(5);
double y = doIt(2);

在这些调用之后,x 将为 9(浮点数为 4,+5),y 为 10(双精度为 8,+2)。

这里有一个更有趣的例子:

auto doIt2 = [] (long l) {

    return makeReturnConverter([=] (auto t) {
        decltype(t.get()) x;
        for (std::size_t i = 0; i != l; ++i ) {
            x.push_back(i*i);
        }
        return x;
    });
};

std::vector<int> x = doIt2(5);
std::deque<int> y = doIt2(5);

这里,只要标准容器有push_back方法,我就可以按照左边的要求一般地构建一个标准容器。

就像我在开始时所说的那样,在绝大多数情况下,这过于复杂且难以证明其合理性。通常您可以将特定类型作为显式(非推断)模板参数和auto 左侧。不过,我已经在非常具体的情况下使用了它。例如,在 gtest 中,当您声明测试夹具时,任何重用的数据都被声明为夹具中的成员变量。非静态成员变量必须用它们的类型声明(不能使用 auto),所以我使用这个技巧来允许快速构建某些类型的夹具数据,同时保持重复尽可能接近零。在这个用例中没问题,因为该代码不需要非常健壮,但确实希望不惜任何代价最小化重复;通常这不是一个很好的权衡(并且通常不需要左侧可用的auto)。

【讨论】:

  • 可能:template &lt;class T&gt; struct identity { using type = T; };typename decltype(t)::type 而不是 decltype(t.get())
  • @Jarod42 我从那个开始,最后似乎更长了。
【解决方案2】:

这里的答案是否定的。

返回的类型基于函数内部使用的值

auto dothings = [](long position) {
auto variable;        // This is illegal as the compiler can not deduce the type.
                      // An auto declaration needs some way for it to determine the type.
                      // So that when you use it below it can determine the
                      // return type of the function.
/*do things*/
return variable;
};

赋值运算符查看结果表达式的类型,以查看是否有任何自动转换可用于将函数结果类型转换为赋值类型的目标。

char x = dothings(10); // the result of the expression on the right
                       // is converted to char before assignment. If
                       // it can't be converted then it is a compiler error.

您可以将 lambdas 视为创建函子的语法糖。

[<capture List>](<Parameter List>) {
    <Code>
}

等同于:

struct <CompilerGeneratedName>
{
    <Capture List With Types>;
    Constructor(<Capture List>)
         : <Capture List With Types>(<Capture List>)
    {}
    <Calculated Return> operator()(<Parameter List>) const {
        <Code>
    }
}{<Capture List>};

例子:

{
    int y = 4;
    auto l1 = [&y](int x){return y++ + x;}
    struct MyF
    {
        int& y;
        MyF(int& y)
            : y(y)
        {}
        int operator()(int x) const {
            return y++ + x;
        }
    };
    auto l2 = MyF(y);

    std::cout << l2(5) << " " << l1(5) << "\n";
}

【讨论】:

  • 我不知道你为什么得不到爱。这是对所述问题的正确答案。
【解决方案3】:

我知道的唯一方法是使用模板参数:

template<typename T> 
T dothings(long value)
{
    T result;
    ...
    return result;
}

auto x = dothings<float>(1l);
auto y = dothings<char>(2l);

该模板还允许您专门针对某些类型。因此,如果您想要双打的不同逻辑,您可以编写以下内容:

template<>
double dothings<double>(long value)
{
    T result;
    ...
    return result * 2.0;
}

【讨论】:

  • 我在主帖“是否有与模板类型名称等效”中提到它,专门询问了 lambda,但谢谢。
  • 这没有回答问题。不回答问题的替代品应该是 cmets。
  • 明确地给出类型基本上与推断它完全相反。
猜你喜欢
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多