【问题标题】:C++ lambda, not seeing function and argument?C++ lambda,没有看到函数和参数?
【发布时间】:2015-06-21 16:08:40
【问题描述】:

我有一些代码使用std::thread 周围的包装类,它使用计时器结构(基于boost::asio)每5000 毫秒调用一次methodToCallEachIteration()

class OurThreadWrapperClass{
    OurThreadWrapperClass(boost::asio::io_service& = generic_timer_queue_s());
};

class A {
    A() : thread1(_TimerIOService){
        thread1.setInterval(5000);
        // This sets the callback function, to be called every INTERVAL ms.
        thread1.start([this](OurThreadWrapperClass&) {
            this->methodToCallEachIteration();
        });
    }

    void callAFunctionHere(std::bitset<10> arg) {
        // ...
    }

    void methodToCallEachIteration() {
        // ...
    }

    struct TimerService {
        constexpr static const size_t num_threads{2};

        TimerService(){
            for(auto& t: _threads){
                t = std::thread([this](){
                    boost::asio::io_service::work keepalive{_ioservice};

                    callAFunctionHere(_anArgument);  // The method and arg not recognised

                    (void)keepalive;
                    _ioservice.run();
                });
            }
        }

        operator boost::asio::io_service&() {
            return _ioservice;
        }

        boost::asio::io_service _ioservice{num_threads};
        std::thread _threads[num_threads];
    };

    OurThreadWrapperClass thread1;
    TimerService _TimerIOService;
    std::bitset<10> _anArgument;
};

我遇到的问题是我想从准备线程的 TimerService 中调用callAFunctionHere()。我无法在 TimerService 中移动此功能。但是,编译器抱怨它找不到callAFunctionHere()_anArgument

error: cannot call member function callAFunctionHere(std::bitset<10>) without object

error: 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = TimerService::TimerService()::__lambda19; _Args = {}]', declared using local type TimerService::TimerService()::__lambda19', is used but never defined [-fpermissive]
       thread(_Callable&& __f, _Args&&... __args)

我认为我需要更改 A::A() 中的 lambda,以便编译器可以“看到”方法和参数,但我不太确定如何?

【问题讨论】:

  • 你可能想在这里重新考虑你的封装。 ATimerService 的功能似乎很混乱,如果意图是让 TimerService 中的功能可在其他类中重复使用,那么将其设置为 A 的嵌套类似乎是多余的。
  • BTW thread1_TimerIOService 之前构造,因此您将未初始化的对象传递给 thread1 的构造函数。

标签: c++ multithreading c++11 lambda boost-asio


【解决方案1】:

一如既往,将问题分解为 MVCE:

class A {
    void callAFunctionHere() {}

    struct TimerService {
        TimerService() {
            callAFunctionHere();
        }
    };
};

int main()
{
    A a;
    A::TimerService ts;
}

http://ideone.com/lEUCvO

prog.cpp: In constructor 'A::TimerService::TimerService()':
prog.cpp:6:35: error: cannot call member function 'void A::callAFunctionHere()' without object
             callAFunctionHere();

此特定错误告诉您,由于名称解析,编译器可以告诉您您正在尝试访问外部范围 (class A) 的函数/成员,但您是从上下文中进行的instance 的内部 (class A::TimerService)。

这行代码:

 callAFunctionHere(_anArgument);

写在A::TimerService::TimerService()里面,所以this指针是(A::TimerService*) this,代码展开为:

 A::callAFunctionHere(A::_anArgument);

this 不能应用于其中任何一个,因为AA::TimerService 之间没有直接继承或转换。

这些类被嵌套用于命名目的,就像namespace,但它们在其他方面是离散的:仅仅因为TimerService 是在A 内部声明的,所以不会连接它们在名称之外的实例作用域:它不建立继承或转换关系

也许你想做的是创建一个通用的TimerService 类,它可以通过继承来描述可重用的功能:

#include <iostream>

struct TimerService
{
    virtual void handleTimerEvent() = 0;

    TimerService()
    {
    }

    void trigger()
    {
        handleTimerEvent();
    }
};

class A : public TimerService
{

    const char* m_name;

public:
    A(const char* name_) : TimerService(), m_name(name_) {}

    void handleTimerEvent() override
    {
        std::cout << "Handling timer event in " << m_name << '\n';
    }
};

void triggerEvent(TimerService& service)
{
    service.trigger();
}

int main()
{
    A a("A instance 'a'"), b("A instance 'b'");
    triggerEvent(b);
    triggerEvent(a);
}

http://ideone.com/92aszw

【讨论】:

  • 嗨,有没有办法修复上面的代码,以便 TimerService 可以保存指向 A 的指针?
  • 你可以,但它没有多大意义。在上面的示例中,A 的实例是TimerService 的实例,带有额外的A 特定的东西。因此,您不需要指向A 的指针,只需使用virtual 函数来调用TimerService 不应该提供的任何内容,并在A 中实现它们。现在,TimerService 可以在不提供实现的情况下引用这些函数,并且在运行时对象实例将提供正确的函数 - 如所示。见handleTimerEvent
  • 不完全清楚您为什么要尝试将TimerServiceA 分开。你的另一种选择是只做这门课。 boost::asio 和线程系统不在乎你调用你的类。
  • 这里有一个更全面的示例,它使用基类来描述实现所满足的概念:ideone.com/5Wy1ce。我鼓励你在继续你的线程挑战之前阅读类、继承、封装、虚函数、指针和引用。您的代码似乎表明您来自另一种语言,可能是 C,并且您已经将 C++ 理解为与所述其他语言相似,但实际上并非如此。
【解决方案2】:

在调用callAFunctionHere 的lambda 中,捕获的thisclass A::TimerService 实例的class A::TimerService,但您正试图隐式使用class A 实例的成员。您需要引用A 类型的对象,并使用该对象的成员。

class A {
    A() : _TimerIOService(this), thread1(_TimerIOService){
        //...
    }
    //...
    struct TimerService {
        A *a_;
        constexpr static const size_t num_threads{2};

        TimerService(A *a) : a_(a) {
            //...
    //...
    TimerService _TimerIOService;
    OurThreadWrapperClass thread1;
    std::bitset<10> _anArgument;
};

注意_TimerIOService 需要在thread1 之前。 现在,A::TimerService 中的 lambda 使用 a_ 访问所需的成员。

【讨论】:

  • 嗨,A 对象不是在 A ctor 的 lambda 调用期间传入的吗? [this] 位?
  • TimerService 中的“this”与“this”无关。
  • class Aclass A::TimerService 是两种不同的类型,彼此之间没有任何隐含关系。在A 内定义TimerService 只是限定类名的范围。没有内部类的内部实例可以访问定义该类的包含类的任何形式的概念。
  • 您能举例说明 Timerservice 部分如何接受 lambda 更改吗?
  • 我会更改 TimerService::TimerService() 以获取 A 指针或引用并将该引用存储在 A::TimerService 本身中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-20
  • 2016-09-17
  • 1970-01-01
  • 1970-01-01
  • 2021-03-28
  • 1970-01-01
相关资源
最近更新 更多