c++ - 从C++ 17开始的prvalue差异中的直接列表初始化与复制列表初始化与复制初始化的示例

标签 c++ initialization c++17 list-initialization

对于这3种初始化形式(对于某些A类型)

A x = {<...>};
A x{<...>};
auto x = A{<...>};

自C++ 17以来的行为差异示例,除了w.r.t.显式ctor(显式ctor会破坏第一个)?

(由于C++ 17保证复制删除可以消除复制;
由于auto ...,复制构造函数的隐式转换也不应在此处应用)

我一般都知道C++初始化形式是如何工作的,对于这个问题的广泛性/模糊性,我感到很抱歉,我只是想知道上面三种形式之间是否有任何(重大)区别,或者也许它们可以互换使用(区别除外)提到)。

最佳答案

我认为这里的差异很重要,尤其是由于隐式/显式差异,我相信您太快不敢理会了。

我要回答的问题与您提出的问题略有不同:为什么使用哪种形式?相对于:有什么区别?

A x = {<...>};

如果可以编译,请首选此格式。

如果rhs表达式具有可显式转换为A的类型,则不会编译。那是一件非常好的事!这是该语言提供的安全功能。

类型作者通常(或至少应该)保留隐式构造/转换,以进行非常安全的转换。这样的转换应该是无损的,并且不会改变rhs和lhs之间表达的基本语义。例如:milliseconds x = 3s; rhs是seconds,lhs是milliseconds。并且构造在两个持续时间之间转换,并且完全不丢失任何信息。这是使用这种结构形式的好地方。

通过使用这种形式,程序员说:只给我A类型的构造函数的“安全集”。如果rhs上的表达式类型有错误,可能导致不安全的转换,我想在编译时进行查找。

A是整数类型时,即使包括{}也有一个优势:
unsigned x = {i};

这是遵循只给我安全转换的说法。但这给您的吊带带来了额外的麻烦:请给我带来无限的转换机会。 {}本质上是后者C++中的一个额外的权宜之计,以弥补几十年前C语言中的设计错误。

但是有时您需要更大的锤子。有时,明确的转换是您想要和需要的。现在是时候:
A x{<...>};

例如:milliseconds x{3};它将int转换为millisecond。尽管转换是无损的,但lhs的类型与rhs的类型不同。 rhs可以代表3个字。 3个苹果。 3国税局的通知。 3年。这不是一个隐式进行的安全转换。而且std::lib知道这一点。如果尝试过,它将无法编译。但是,有时候这正是您需要做的。为此情况保留此表格。

在某些情况下,不遵循此建议可能会导致运行时错误。 this lightning talk中演示了其中的两个。

最后,这是一个很好的形式:
auto x = A{<...>}; 

当您希望x的类型为A时。当A并非易于拼写的类型,甚至不出现在rhs上时,它特别好。

我最喜欢使用这种形式的示例是在std::chrono::round实现中:
template <class To, class Rep, class Period>
constexpr
To
round(const duration<Rep, Period>& d)
{
    auto t0 = floor<To>(d);
    auto t1 = t0 + To{1};
    if (t1 == To{0} && t0 < To{0})
        t1 = -t1;
    auto diff0 = d - t0;  // here
    auto diff1 = t1 - d;  // and here
    if (diff0 == diff1)
    {
        if (t0 - duration_cast<To>(t0/2)*2 == To{0})
            return t0;
        return t1;
    }
    if (diff0 < diff1)
        return t0;
    return t1;
}

标记为“here and here”的行通常导致diff0diff1的类型非常复杂:有两种不同的拼写方式。这是common_type_v<duration<Rep, Period>, To>。对于代码的读者来说,确切地知道类型是什么并不重要。要知道的唯一重要的事情是,此类型将表示两个操作数的确切区别。

总之,这不是“最佳形式”。它们都是工具箱中的好工具。诀窍是知道何时使用哪个。而且,如果您擅长于此,那么您将比大多数C++程序员更加熟练。

关于c++ - 从C++ 17开始的prvalue差异中的直接列表初始化与复制列表初始化与复制初始化的示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61470360/

相关文章:

c++ - 是否可以为成员的方法编写 using 声明,类似于固有方法?

c++ - 使用 libpcap 进行无线数据包注入(inject)

python - 从 pycuda.compiler 导入 SourceModule

c++ - 在其定义中引用变量是否合法?

c++ - Vcpkg 无法在带有 mingw 的 Windows 上运行

c++ - 构建行为排序函数

C++ 捕捉悬空引用

c++ - 如何使用 lock_guard 在 c++11 中实现 scoped_lock 功能

iOS 指定初始化器 : Using NS_DESIGNATED_INITIALIZER

c++ - 获取错误无法从C++ 17编译器的间接库继承构造函数