• C++‘s most vexing parse


    C++'s most vexing parse

    直接看一个例子:

    #include
    #include
    #include 
    
    using namespace std::chrono_literals;
    
    class task {
        public:
            void operator() () {
                std::cout << "task begin..." << std::endl;
                std::this_thread::sleep_for(2s);
                std::cout << "task done!" << std::endl;
            }
    };
    
    int main() {
        std::thread t(task());
        t.join();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这段代码很简单,在主线程(main函数)中起动一个子线程,并等待子线程执行完成。然而,上面代码编译会报错:

    test.cpp: In function ‘int main()’:
    test.cpp:18:7: error: request for member ‘join’ in ‘t’, which is of non-class type ‘std::thread(task (*)())’
       18 |     t.join();
          |       ^~~~
    
    • 1
    • 2
    • 3
    • 4

    因为 t 根本就不是一个类类型的对象,而是一个函数,这里的 std::thread t(task()) 并非创建一个 std::thread 对象并启动该线程执行,而是申明了一个函数,该函数的返回值类型为 std::thread ,函数名为 t,参数为一个函数指针(返回值类型为 task,无函数名,参数为空)。这就是本文的主题:C++'s most vexing parse(C++最令人费解的解析)。Scott Meyers 在《Effective STL》中的 Item 6 有相关介绍。

    在 C++ 中,加入我们要申明一个函数 f ,返回值类型为 int,参数类型为 double,则以下 3 种都是合法的申明方式:

    int f(double d);
    int f(double (d));
    int f(double);
    
    • 1
    • 2
    • 3

    第二个申明,对行参加上圆括号,编译器会认为是冗余的并忽略。第三个省略了行参名。

    如果我们想申明一个函数,并且其参数是一个函数指针,下面 3 种写法都是合法的:

    int g(double (*pf)());
    int g(double pf());
    int g(double ());
    
    • 1
    • 2
    • 3

    第二个申明中行参省去指针类型也是合法的,第三个申明中行参直接省去了函数名也是合法的。

    更普遍地,对于下面的语句:

    T1 name(T2());
    T1 name1(T2(name2));
    
    • 1
    • 2

    C++都会将其视为函数申明:

    • 对于第一个语句,C++将其视为:返回值类型为 T1 名为 name 的函数(参数类型为指向返回值类型为 T2,参数为空的函数的指针)。
    • 对于第二个语句,C++将其视为:T1 name1(T2 name2); 显然也是一个函数申明。

    再看本文开头给出的例子,就不难理解编译报错的原因了。解决办法也比较多,比如:

    std::thread t((task()));
    
    std::thread t{task()};
    
    task f
    std::thread t(f);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    至此,本文结束,希望对你有所帮助。

    参考:

    • Scott Meyers 《Effective STL》
    • http://eel.is/c++draft/dcl.ambig.res
    • https://www.fluentcpp.com/2018/01/30/most-vexing-parse/
  • 相关阅读:
    【AI Business Model】人工智能的定义 | 了解 AI 的历史 | 简单理解什么是 “图灵测试“
    Web3:开源文化下的技术创新
    OA项目之我的审批(查询&会议签字)
    LOPOCS 显示 PG 中的点云数据
    安装opencv4.5.5
    通过 vue-cli 创建一个 vue3 的项目
    在 360 度绩效评估中应该问的 20 个问题
    Java 集合框架:LinkedList 的介绍、使用、原理与源码解析
    Otter改造 增加springboot模块和HTTP调用功能
    Windows10安装blender教程
  • 原文地址:https://blog.csdn.net/Dong_HFUT/article/details/126435993