Lambda类型推导

时间:2018-08-29 00:14:30

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

auto dothings = [](long position) {
auto variable;
/*do things*/
return variable;
};

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

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

3 个答案:

答案 0 :(得分:4)

可以做到,但这是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)。

答案 1 :(得分:3)

这里的答案是否定的。

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

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.

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

[<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";
}

答案 2 :(得分:0)

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

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;
}