直接初始化!=从不同类型转换时复制初始化?

gla*_*des 2 c++ copy-initialization packaged-task direct-initialization

我一直认为对于与类类型不匹配的类型 T 的直接初始化和复制初始化是绝对相等的。但我似乎误会了。如果我复制初始化(使用=),下面的代码不会编译,并且只有当我通过括号()直接初始化时才编译(在任何情况下,代码在终止时都不起作用,但这是一个不同的故事,与这个问题)。

演示

#include <future>
#include <cstdio>

int main()
{
    /* This doesn't compile */

    // std::packaged_task<int()> foo = []() -> int {
    //     return 10;
    // };

    /* This works */

    std::packaged_task<int()> foo([]() -> int {
        return 10;
    });

    auto fut = foo.get_future();
    foo();
    auto a = fut.get();
    printf("a == %d\n", a);
}
Run Code Online (Sandbox Code Playgroud)

错误:

<source>: In function 'int main()':
<source>:8:37: error: conversion from 'main()::<lambda()>' to non-scalar type 'std::packaged_task<int()>' requested
    8 |     std::packaged_task<int()> foo = []() -> int {
      |                                     ^~~~~~~~~~~~~
    9 |         return 10;
      |         ~~~~~~~~~~                   
   10 |     };
      |     ~   
Run Code Online (Sandbox Code Playgroud)

cppreference对于复制初始化声明以下内容:

对于 T = U 的情况:

否则,如果 T 是类类型,并且 other 类型的 cv 未限定版本不是 T 或派生自 T,或者如果 T 是非类类型,但 other 类型是类类型,则用户定义检查可以从 other 的类型转换为 T 的转换序列(如果 T 是类类型并且转换函数可用,则转换为从 T 派生的类型),并通过重载决策选择最佳的转换序列。转换的结果,它是 cv 未限定版本的右值临时 (C++11 之前) 纯右值临时 (C++11 起)(C++17 之前) 纯右值表达式 (C++17 起) T 如果使用了转换构造函数,则用于直接初始化对象。最后一步通常被优化,转换结果直接在为目标对象分配的内存中构造,但即使不使用适当的构造函数(移动或复制)也需要可访问。(C++17 之前)

如此处所述,我希望 的构造函数std::package_task(它采用与 基本相同的可调用项std::function)将提供一个可用的转换序列,因为 lambda 可以转换为std::packaged_task,例如直接初始化的情况。但这似乎并没有发生。我在忽略什么?

for*_*818 5

这是由于being的构造函数std::packaged_task<int()>explicit造成的。来自cppreference/explicit

指定构造函数或转换函数 (C++11 起) 或推导指南 (C++17 起) 是显式的,即它不能用于隐式转换和复制初始化。

构造函数是完美匹配(模板参数T匹配任何内容),但它不是可行的用户定义转换序列(并且也没有其他可行的从 lambda 到 的转换std::packaged_task<int()>)。它失败的原因与此相同:

struct foo { };

struct bar {
    explicit bar(foo){}
};

int main() {
    foo f;
    bar b = f;
}
Run Code Online (Sandbox Code Playgroud)

居住

<source>:9:13: error: conversion from 'foo' to non-scalar type 'bar' requested
    9 |     bar b = f;
      |             ^
Run Code Online (Sandbox Code Playgroud)

同时,删除explicithttps://godbolt.org/z/cPx97zx1e)或使用bar b(f);https://godbolt.org/z/WMYrb18P8)并不是错误。

请注意,当用模板化构造函数替换上面的构造函数时,事情不会改变(错误/没有显式/显式调用构造函数)。

  • @glades,这就是 `explicit` 的作用:https://en.cppreference.com/w/cpp/language/explicit (2认同)
  • @glades `bar b= f;` 需要进行有效的转换,但您需要显式调用它,如 `bar b = bar(f);` 中。使用“bar b(f)”,您显式调用构造函数 (2认同)
  • @glades“构造函数”足够通用,它涵盖了非模板和模板化构造函数 (2认同)