对于这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”的行通常导致
diff0
和diff1
的类型非常复杂:有两种不同的拼写方式。这是common_type_v<duration<Rep, Period>, To>
。对于代码的读者来说,确切地知道类型是什么并不重要。要知道的唯一重要的事情是,此类型将表示两个操作数的确切区别。总之,这不是“最佳形式”。它们都是工具箱中的好工具。诀窍是知道何时使用哪个。而且,如果您擅长于此,那么您将比大多数C++程序员更加熟练。
关于c++ - 从C++ 17开始的prvalue差异中的直接列表初始化与复制列表初始化与复制初始化的示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61470360/