什么功能使我可以将auto
用于C ++ 17中的不可复制(和不可移动)类型,而不能用于C ++ 14?
请考虑以下代码:
struct A{
A(A const&)=delete;
A(A&&)=delete;
};
int main(){
auto a1 = A{}; // ok in C++17, not ok in C++14
auto&& a2 = A{}; // ok in C++17, ok in C++14
}
事实证明,这在C ++ 14中是无效的代码,但在C ++ 17中是有效的。该行为在clang和gcc中是一致的:https://godbolt.org/z/af8mEc
我问的原因是因为直到最近我才使我的类(代表引用)不可复制,其中除其他方面还禁止使用auto
,但不幸的是,事实证明现在该技术在C语言中不起作用++ 17。
(换句话说,我认为C ++ 14中的行为在概念上是正确的。)]
为什么auto a1 = A{};
对于C ++ 17中的不可复制类有效?是某种新的RVO机箱吗?
我认为auto
在语义上由于多种可辩驳的原因而被破坏,但是至少在C ++ 14中,我可以阻止使用auto
(但允许使用auto&&
)。
是否有另一种方法可以防止对C ++ 17中的特定类使用auto a = A{};
,还是不再使用?
Note:不久前我问了这个问题Is there a way to disable auto declaration for non regular types?,发现当时的解决方案是禁用C ++ 14中的copy and move构造函数,这在概念和语法上都有意义,但这不是C ++ 17中不再有这种情况。
在C ++ 17中,如果为用户提供某种类型的prvalue,则用户可以always使用它来初始化该类型的变量。]>
本质上是the definition of a prvalue has changed。在C ++ 14中,它是一个对象。具体来说,是一个临时对象。因此,类似A a = prvalue_of_type_A;
的语句意味着将临时对象移至a
。可以忽略该移动,但是从逻辑上讲这是一个移动,因此A
必须支持移动构造。
在C ++ 17中,prvalue仅仅是对象的initializer
。初始化哪个对象取决于如何使用它。使用prvalue初始化prvalue类型的变量意味着您初始化了该对象。没有副本或移动。您可以从prvalue是一个无名对象的角度来看它,A a = prvalue_of_A;
只是给该对象起一个名字,而不创建一个新对象。而且不,您无法解决它。只要您要处理某种类型的真正prvalue,auto a = prvalue;
总是会推断出prvalue的类型并直接初始化对象a
,而无需复制/移动。
换句话说,我认为C ++ 14中的行为在概念上是正确的。
嗯,让我们调查一下。
此调查开始并结束时完全意识到您没有阻止auto a = A{};
正常工作。您阻止了复制/移动构造,该构造具有防止该语法的side-effect
通过拒绝一种类型的复制/移动构造功能,您确实确实避免了这种语法。但是在此过程中发生了很多附带损害。
您链接到的帖子给出了由于类型为non-Regular而希望禁用此语法的基本原理,出于某种原因,这使auto
成为“ tricky”。忽略那些未说明的原因是否有效,您的解决方案不仅删除了“棘手的”语法。它阻止您对对象执行基本操作。
考虑将其应用于string_view
的后果。您已经有了这种视图类型,并且想要通过一个多步骤过程对其进行修改。但是您想保持原始视图不变。所以自然地,您复制...哦,等等,string_view
是非常规的,因此您为了防止string_view sv = prvalue;
而将其复制为非法。哎呀。因此,现在我必须回到string_view
的原始来源来获取另一个。假设我可以访问源,则不只是将其作为const&
参数传递。
基本上,您是说拍打苍蝇的最佳方法是使用大锤。不管苍蝇有多烦人,墙[[其后
可能都更重要。所以,保证省略在概念上正确吗?好吧,保证省略的主要理由之一是允许返回prvalue的函数甚至对于不动的类型也可以工作。这实际上很重要。在C ++ 17之前,如果您想为类提供工厂功能,但从逻辑上讲类本身应该是固定不变的,那么您就不走运了。您可以选择其中一个,但不能两个都选。让我们以您的subrange
为例。在C ++ 14中,如果我想编写一个仅为某些构造函数的参数提供默认值的函数,或者使用特定的容器等,那是不可能的。我什至无法编写一个容器,该容器具有
returned
一个subrange
的功能,因为我必须构造一个(除非我使用了原始的括号初始化列表,但这并不总是可能的) )会引起复制/移动。保证的修正解决了所有这些问题。您可以使用只能通过工厂创建的私有构造函数来制作固定类。您可以创建通过公共接口构建固定对象的函数。依此类推。C ++ 17的行为使固定的非常规类型更加有用和有能力。那不是“概念上正确”吗?