在ä¿è¯å¤åˆ¶çœç•¥çš„情况下,为什么需è¦å®Œå…¨å®šä¹‰ç±»ï¼Ÿ

时间:2018-06-27 08:31:54

标签: c++ language-lawyer c++17

对this post的跟进。请考虑以下内容:

class C;
C foo();

那是一对有效的声明。仅声明函数时,Cä¸éœ€è¦å®Œå…¨å®šä¹‰ã€‚但是,如果我们è¦æ·»åŠ ä»¥ä¸‹åŠŸèƒ½ï¼š

class C;
C foo();
inline C bar() { return foo(); }

然åŽçªç„¶C必须是完全定义的类型。但是在ä¿è¯å¤åˆ¶çœç•¥çš„情况下,ä¸éœ€è¦ä»»ä½•æˆå‘˜ã€‚没有å¤åˆ¶ç”šè‡³æ²¡æœ‰åŠ¨é™ï¼Œè¯¥å€¼åœ¨å…¶ä»–地方åˆå§‹åŒ–,并且仅在调用方(到bar)的上下文中销æ¯ã€‚

那为什么呢?标准中ç¦æ­¢ä½¿ç”¨ä»€ä¹ˆï¼Ÿ

4 个答案:

答案 0 :(得分:14)

出于兼容性原因和/或效率的考虑,ä¿è¯çš„å¤åˆ¶çœç•¥ä¹Ÿæœ‰ä¾‹å¤–。å³ä½¿å¯ä»¥å¦å¤–ä¿è¯å¤åˆ¶çœç•¥ï¼Œä¹Ÿå¯ä»¥å¤åˆ¶ç碎的å¯å¤åˆ¶ç±»åž‹ã€‚没错,如果这ä¸é€‚用,那么编译器将能够生æˆæ­£ç¡®çš„代ç ï¼Œè€Œæ— éœ€äº†è§£C的任何细节,甚至ä¸çŸ¥é“其大å°ã€‚但是编译器确实需è¦çŸ¥é“这是å¦é€‚用,为此,它ä»ç„¶éœ€è¦å®Œæ•´çš„类型。

æ ¹æ®https://timsong-cpp.github.io/cppwp/class.temporary:

  

15.2临时对象[class.temporary]

     

已创建1个临时对象

     

[...]

     

(1.2)-在实现需è¦ä¼ é€’或返回普通å¤åˆ¶ç±»åž‹çš„对象时(è§ä¸‹æ–‡ï¼‰ï¼Œå¹¶ä¸”

     

[...]

     

3当类类型X的对象传递给函数或从函数返回时,如果Xçš„æ¯ä¸ªå‰¯æœ¬æž„造函数,move构造函数和æžæž„函数都是ç碎的或删除的,则{{1 }}至少具有一个未删除的副本或移动构造函数,å…许实现创建一个临时对象æ¥ä¿å­˜å‡½æ•°å‚数或结果对象。临时对象分别从函数å‚数或返回值构造,并且函数的å‚数或返回对象被åˆå§‹åŒ–,就好åƒä½¿ç”¨æœªåˆ é™¤çš„ç碎构造函数æ¥å¤åˆ¶ä¸´æ—¶å¯¹è±¡ä¸€æ ·ï¼ˆå³ä½¿è¯¥æž„造函数ä¸å¯è®¿é—®æˆ–å°†ä¸ä¼šè¢«é€‰æ‹©ï¼‰é€šè¿‡é‡è½½åˆ†è¾¨çŽ‡æ‰§è¡Œå¯¹è±¡çš„å¤åˆ¶æˆ–移动)。 [注æ„:此宽容度å…许将类类型的对象传递给寄存器中的函数或从其中返回。 -尾注]

答案 1 :(得分:8)

这与å¤åˆ¶çœç•¥æ— å…³ã€‚ foo应该返回一个C值。åªè¦æ‚¨å°†å¼•ç”¨æˆ–指针传递给foo,就å¯ä»¥ã€‚一旦您å°è¯•è°ƒç”¨foo(就åƒbar一样),它的å‚数大å°å’Œè¿”回值就必须在手;知é“的唯一有效方法是呈现所需类型的完整声明。 如果签å使用的是引用或指针,则所有必需的信æ¯éƒ½ä¼šå­˜åœ¨ï¼Œæ‚¨å¯ä»¥åœ¨æ²¡æœ‰å®Œæ•´ç±»åž‹å£°æ˜Žçš„情况下进行æ“作。这ç§æ–¹æ³•çš„å称是:pimpl ==指å‘IMPLementaion的指针,它被广泛用作éšè—æºä»£ç åº“å‘行版中详细信æ¯çš„一ç§æ–¹æ³•ã€‚

答案 2 :(得分:6)

规则ä½äºŽ[basic.lval]/9:

  

除éžå¦æœ‰è¯´æ˜Žï¼ˆ[dcl.type.simple]),å¦åˆ™prvalue必须始终具有完整类型或void类型; ...

答案 3 :(得分:1)

尽管对此线程å‘布了答案数é‡å’Œè¯„论数é‡ï¼ˆå·²å›žç­”了所有我的个人问题),但我还是决定为“我们其他人â€å‘布答案。我最åˆä¸äº†è§£OP的功能,但现在我知é“了,所以我想分享一下。亲爱的读者,如果您已了解所有这些并感到无èŠï¼Œè¯·ç»§ç»­å‰è¿›ã€‚

@xskxzrå’Œ@hvd有效地回答了这个问题,但是@hvd的帖å­ç‰¹åˆ«æ˜¯ç”¨æ ‡å‡†è¯­ï¼Œå¹¶å‡å®šè¯»è€…知é“按值回报(以åŠæ‰©å±•åRVO)的工作原ç†ï¼Œæˆ‘想并ä¸æ˜¯æ¯ä¸ªäººéƒ½ä¼šè¿™æ ·åšã€‚我以为是的,但是我å´é”™è¿‡äº†ä¸€ä¸ªé‡è¦çš„细节(当您仔细考虑时,它实际上是很明显的,但是ä»ç„¶æˆ‘错过了)。

因此,本文主è¦å…³æ³¨äºŽæ­¤ï¼Œä»¥ä¾¿æˆ‘们大家都å¯ä»¥ç†è§£ä¸ºä»€ä¹ˆï¼ˆa)OP首先想知é“为什么在编译bar()时出现问题,然åŽï¼ˆb)éšåŽæ„识到了原因。

因此,让我们å†æ¬¡æŸ¥çœ‹è¯¥ä»£ç ã€‚鉴于此(å³ä½¿ç±»åž‹ä¸å®Œæ•´ï¼Œè¿™ä¹Ÿæ˜¯åˆæ³•çš„):

class C;
C foo();

为什么编译器无法编译此代ç ï¼ˆæˆ‘删除了inline是因为它无关紧è¦ï¼‰ï¼š

C bar() { return foo(); }

æ¥è‡ªgcc的错误消æ¯æ˜¯ï¼š

  

错误:返回类型“ Cç±»â€ä¸å®Œæ•´

首先,被接å—的答案引用了明确ç¦æ­¢å®ƒçš„标准中的相关段è½ï¼Œå› æ­¤æ²¡æœ‰ä»€ä¹ˆç¥žç§˜ä¹‹å¤„。但是,OP(实际上是å‘表评论的Walter,实际上是评论员Walter)想知é“为什么。

首先,在我看æ¥è¿™å¾ˆæ˜Žæ˜¾ï¼šè°ƒç”¨è€…需è¦ä¸ºå‡½æ•°ç»“果分é…空间,而且它ä¸çŸ¥é“对象有多大,因此编译器å分å¤æ‚。但是我没有一个çªé—¨ï¼Œè€Œé‚£ä¸ªåœ¨äºŽæŒ‰å€¼è¿”回的工作方å¼ã€‚

现在,对于那些ä¸çŸ¥é“的对象,按值返回类对象的方法是,调用者通过在堆栈上为返回的对象分é…空间,并将指å‘该对象的指针作为éšè—å‚数传递给所调用的函数æ¥è¿›è¡Œæž„造,对象,对其进行æ“作,无论如何。

但是,这是èŠèŠ±é“¾ï¼Œå› æ­¤ï¼Œå¦‚果我们具有以下代ç ï¼ˆåœ¨è°ƒç”¨C之å‰æˆ‘们已完全定义bar(),则为该代ç ï¼‰ï¼š

class C
{
public:
    int x;
};

C c = bar ();
c.x = 4;

然åŽåœ¨è°ƒç”¨ c之å‰åˆ†é…bar()的空间,然åŽå°†c的地å€ä½œä¸ºéšè—å‚数传递给bar() ,然åŽç›´æŽ¥ä¼ é€’到foo(),最åŽå°†æž„造的对象填充到所需的ä½ç½®ã€‚因此,由于bar()实际上没有对该指针进行任何æ“作(除了传递指针),因此它关心的åªæ˜¯æŒ‡é’ˆæœ¬èº«ï¼Œè€Œä¸æ˜¯æŒ‡é’ˆæŒ‡å‘的内容。

是å—?好å§ï¼Œå®žé™…上,是的。

按值返回类对象时,å°å¯¹è±¡é€šå¸¸ä¼šä½œä¸ºä¼˜åŒ–在寄存器(或一对寄存器)中返回。在大多数情况下,如果对象足够å°ï¼Œç¼–译器å¯ä»¥é¿å…这样åšï¼ˆç¨åŽä¼šä»‹ç»æ›´å¤šå†…容)。

但是现在, bar()需è¦çŸ¥é“这是å¦æ˜¯foo()è¦åšçš„事情,并且出于å„ç§åŽŸå› ï¼Œå®ƒéœ€è¦æŸ¥çœ‹å…¨éƒ¨å†…容类的声明。

因此,总而言之,这就是为什么编译器需è¦å®Œå…¨å®šä¹‰çš„类型æ‰èƒ½è°ƒç”¨foo()的原因,å¦åˆ™å®ƒå°†ä¸çŸ¥é“foo()会是什么,因此它ä¸çŸ¥é“。ä¸çŸ¥é“生æˆä»€ä¹ˆä»£ç ã€‚无论如何,ä¸â€‹â€‹æ˜¯åœ¨å¤§å¤šæ•°å¹³å°ä¸Šéƒ½å¯ä»¥ã€‚

注释:

  1. 我看了看gcc,似乎有两个(完全逻辑上)的规则æ¥ç¡®å®šæ˜¯åœ¨ä¸€ä¸ªå¯„存器还是一对寄存器中返回一个类对象:

    • a)对象为16个字节或更å°ï¼ˆ64ä½ç‰ˆæœ¬ï¼‰ã€‚
    • b)std::is_trivially_copyable<T>::value的评估结果为true(也许有人å¯ä»¥åœ¨æ ‡å‡†ä¸­æ‰¾åˆ°æœ‰å…³æ­¤å†…容的信æ¯ï¼‰ã€‚
  2. 如果读者ä¸çŸ¥é“,RVO会在其最终的é™æ­¢ä½ç½®ï¼ˆå³è°ƒç”¨è€…分é…çš„ä½ç½®ï¼‰ä¸­ä¾èµ–构造对象。 。这是因为有些对象(例如,我相信std::basic_stringçš„æŸäº›å®žçŽ°ï¼‰ä¼šåœ¨å†…存中四处移动,因此您ä¸èƒ½ä»…将它们构造在方便的地方然åŽmemcpy其他地方。

  3. 如果无法在该最终ä½ç½®æž„造返回的对象(由于编ç è¿”回对象的函数的方å¼ï¼‰ï¼Œåˆ™RVOä¸ä¼šå‘生(怎么办?),请å‚è§ä¸‹é¢çš„实时演示( make_no_RVO()。

  4. 作为点1b的特定示例,如果一个å°å¯¹è±¡åŒ…å«ï¼ˆå¯èƒ½ï¼‰æŒ‡å‘自身或其任何数æ®æˆå‘˜çš„æ•°æ®æˆå‘˜ï¼Œé‚£ä¹ˆæŒ‰å€¼è¿”回它会给您带æ¥éº»çƒ¦ã€‚声明ä¸æ­£ç¡®ã€‚åªéœ€æ·»åŠ ä¸€ä¸ªç©ºå‰¯æœ¬æž„造函数å³å¯ï¼Œå› ä¸ºä»Žé‚£æ—¶èµ·ï¼Œå®ƒä¸å†æ˜¯å¯å¤åˆ¶çš„。但是我想通常是对的,ä¸è¦ä»Žç¼–译器中éšè—é‡è¦ä¿¡æ¯ã€‚

实时演示here。欢迎对此帖å­å‘表所有评论,我将尽我所能回答他们。