• 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/
  • 相关阅读:
    110道 MySQL面试题及答案 (持续更新)
    基于ssm的学生竞赛模拟系统设计与实现-计算机毕业设计源码+LW文档
    操作系统安装在哪里?
    Spring ApplicationListener监听器用法
    优化 C++ 字符串拼接:高效方法与代码示例
    【数据结构】排序(2)快速排序
    解决0-1背包问题(方案二):一维dp数组(滚动数组)
    HTML常用标签
    离群点检测和新颖性检测
    SpringCloud-Gateway网关实现入参统一解密
  • 原文地址:https://blog.csdn.net/Dong_HFUT/article/details/126435993