引入lambda后,是否有任何类内功能用例?

时间:2011-07-29 04:49:05

标签: c++ c++11 nested-class lambda

关于 Lambda函数和表达式的wikipedia文章

  

用户通常希望在该位置附近定义谓词函数   他们在哪里进行算法函数调用。语言只有一个   这个机制:在一个内部定义一个类的能力   功能。 ...函数中定义的类不允许在模板中使用

这是否意味着在C ++ 0x lambda到位后,在函数内使用嵌套结构会被默默地弃用?

另外,上一段中最后一行的含义是什么?我知道嵌套类不能是template;但那条线并不意味着。

5 个答案:

答案 0 :(得分:8)

我不确定我理解你的困惑,但我会陈述所有事实并让你解决它。 :)

在C ++ 03中,这是合法的:

#include <iostream>

int main()
{
    struct func
    {
        void operator()(int x) const
        {
            std::cout << x << std::endl;
        }
    };

    func f; // okay
    f(-1); // okay

    for (std::size_t i = 0; i < 10; ++i)
        f(i) ; // okay
}

但如果我们尝试这样做,那就不是:

template <typename Func>
void exec(Func f)
{
    f(1337);
}

int main()
{
    // ...

    exec(func); // not okay, local classes not usable as template argument
}

这给我们留下了一个问题:我们想要定义用于此函数的谓词,但我们不能将放在函数中。所以我们不得不把它移到任何外围的范围并在那里使用它。这些杂乱的东西不仅没有其他人需要知道的东西,而且它使谓词远离它的使用位置,使得阅读代码变得更加困难。

对于函数中偶尔重用的代码块,它仍然可能有用(例如,在上面的循环中;你可以通过其参数将函数谓词添加到某个复杂的东西中),但大部分时间我们都想在模板中使用它们。

C ++ 0x更改规则以允许上述代码工作。他们还添加了lambdas:用于将函数对象创建为表达式的语法,如下所示:

int main()
{
    // same function as above, more succinct
    auto func = [](int x){ std::cout << x << std::endl; };

    // ...
}

这与上面完全相同,但更简单。那么我们还有什么用于“真正的”本地课程吗?当然。毕竟Lambda没有完整的功能:

#include <iostream>

template <typename Func>
void exec(Func func)
{
    func(1337);
}

int main()
{
    struct func
    {
        // note: not possible in C++0x lambdas
        void operator()(const char* str) const
        {
            std::cout << str << std::endl;
        }

        void operator()(int val) const
        {
            std::cout << val << std::endl;
        }
    };

    func f; // okay
    f("a string, ints next"); // okay

    for (std::size_t i = 0; i < 10; ++i)
        f(i) ; // okay

    exec(f); // okay
}

也就是说,对于lambda来说,你可能不会比以前更多地看到本地课程,但出于完全不同的原因:一个几乎无用,另一个几乎被取代。

答案 1 :(得分:3)

定义函数内部的结构从来都不是处理缺少谓词的一种特别好的方法。它有效,如果你有一个虚拟基础,但它仍然是一个非常丑陋的方式来处理事情。它可能看起来像这样:

struct virtual_base {
  virtual void operator()() = 0;
};

void foo() {
  struct impl : public virtual_base {
    void operator()() { /* ... */ }
  };

  register_callback(new impl);
}

如果你想要的话,你仍然可以继续使用这些类 - 内部函数 - 它们不会被弃用或瘫痪;他们从一开始就受到限制。例如,在C ++ 0x之前的C ++版本中,此代码是非法的:

void foo() {
  struct x { /* ... */ };
  std::vector<x> y; // illegal; x is a class defined in a function
  boost::function<void()> z = x(); // illegal; x is used to instantiate a templated constructor of boost::function
}

这种用法在C ++ 0x中实际上是合法的,所以如果有的话,内部类的实用性实际上已经扩展了。尽管如此,它仍然不是一种很好的做事方式。

答案 2 :(得分:3)

  

引入lambda后,函数内部是否有任何用例?

当然。在函数内部有一个类是:

  • 将其本地化为打算使用它的代码的私有实现细节,
  • 阻止其他代码使用并依赖于它,
  • 独立于外部名称空间。

显然有一个阈值,在函数内部有一个大类会损害可读性并混淆函数本身的流程 - 对于大多数开发人员和情况来说,阈值非常低。对于一个大类,即使只有一个函数可以使用它,将两者放入一个单独的源文件可能更清晰。但是,这只是调整品味。

您可以将此视为在类中具有私有函数的反转:在这种情况下,外部API是类的公共接口,函数保持私有。在这种情况下,该函数使用一个类作为私有实现细节,后者也保持私有。 C ++是一种多范式语言,适当地为程序组织和API暴露的层次结构建模提供了这种灵活性。

示例:

  • 函数处理一些外部数据(想想文件,网络,共享内存......),并希望在I / O期间使用类来表示二进制数据布局;如果它只有几个字段并且对其他函数没用,它可能决定将该类设为本地
  • 一个函数想要对一些项进行分组并分配它们的数组,以支持它为获取其返回值所做的内部计算;它可以创建一个简单的结构来包装它们。
  • 一个类被给予一个令人讨厌的按位枚举,或者想要重新解释一个floatdouble来访问mantisa / exponent / sign,并在内部决定使用{{来模拟该值为方便起见,使用合适宽度的位域(注意:实现定义的行为)
  函数中定义的

类不允许在模板中使用它们

我认为你评论说别人的答案已经解释了这一点,但无论如何......

struct

答案 3 :(得分:2)

Boost.Variant。

Lambdas不适用于变体,因为变体需要具有多个operator()(或具有模板化运算符())的对象。

0x允许现在在模板中使用本地类,因此boost::apply_variant可以使用它们。

答案 4 :(得分:2)

正如Tony所提到的,函数内部的一个类不仅仅是谓词。除了其他用例之外,它还允许创建一个工厂函数,该函数创建确认接口的对象,而不暴露实现类。见这个例子:

#include <iostream>
/* I think i found this "trick" in [Alexandrescu, Modern C++ Design] */
class MyInterface {
    public:
        virtual void doSomethingUseful() = 0;
};

MyInterface* factory() {
    class HiddenImplementation : public MyInterface {
        void doSomethingUseful () {
            std::cout << "Hello, World!" << std::endl;
        }
    };

    return new HiddenImplementation();
}


int main () {
    auto someInstance = factory();

    someInstance->doSomethingUseful();
}