非const复制构造函数和返回值的隐式转换

Eti*_*mps 7 c++ gcc copy-constructor implicit-conversion

考虑以下C++代码:

struct B { };
struct A
{
        A(int);
        A(A&); // missing const is intentional
        A(B);
        operator B();
};

A f()
{
        // return A(1); // compiles fine
        return 1; // doesn't compile
}
Run Code Online (Sandbox Code Playgroud)

这在MSVC++ 2010上编译得很好(实际上,在MSVC上,如果我B完全删除它甚至可以工作).它不在GCC 4.6.0上:

conv.cpp: In function ‘A f()’:
conv.cpp:13:9: error: no matching function for call to ‘A::A(A)’
conv.cpp:13:9: note: candidates are:
conv.cpp:6:2: note: A::A(B)
conv.cpp:6:2: note:   no known conversion for argument 1 from ‘A’ to ‘B’
conv.cpp:5:2: note: A::A(A&)
conv.cpp:5:2: note:   no known conversion for argument 1 from ‘A’ to ‘A&’
conv.cpp:4:2: note: A::A(int)
conv.cpp:4:2: note:   no known conversion for argument 1 from ‘A’ to ‘int’
Run Code Online (Sandbox Code Playgroud)

令我困惑的是这个消息no known conversion for argument 1 from ‘A’ to ‘B’.考虑到A::operator B()定义非常明确,这怎么可能呢?

Pup*_*ppy 5

因为你不能做多个隐式转换.你必须A::A(A::A(int)::operator B())去做这项工作,这对于编译器来说是太多的步骤来弄清楚它自己.

  • 重要的是要注意,您不允许的是进行多个**用户定义的**转换,而不是隐式转换.还有其他标准转换是隐式的,可以链接.`A foo(){返回5.0; ``将开始从`double`转换为`int`(隐式浮点转换),然后是用户定义的转换`A(int)`.您可以计算出其他示例,其中可能包括最多3个非用户定义的转换,然后是1个用户定义的转换,最多3个其他非用户定义的转换,在用户定义的转换之后. (3认同)

Dam*_*mon 5

我不认为DeadMG指出的"太多步骤可以自行计算"是原因.我有3-4次转换的构造,编译器总是把它们搞得很好.

我认为问题在于,不允许编译器代表自己将const引用转换为非const引用(只有当您使用强制转换明确告诉它时才允许这样做).
并且由于对传递给复制构造函数的临时对象的引用是const,但复制构造函数不是,它找不到合适的函数.

编辑:我没有找到任何"真正的"代码(见下面的评论),但构建了一个多zigzag转换示例,实际编译没有错误在gcc 4.5下.请注意,这也可以很好-Wall -Wextra地编译,这让我感到惊讶.

struct B
{
    signed int v;
    B(unsigned short in) : v(in){}
};

struct C
{
    char v;
    C(int in) : v(in){}
};

struct A
{
    int v;
    A(B const& in) : v(in.v){}
    operator C() { return C(*this); }
};

enum X{ x = 1 };

int main()
{
    C c = A(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*eas 4

被拒绝的候选人名单上的错误非常明显。问题在于,C++ 语言中涉及用户定义转换的隐式转换序列仅限于单个用户定义转换:

\n
\n

\xc2\xa713.3.3.1.2 [over.ics.user]/1 用户定义的转换序列由初始标准转换序列、用户定义转换 (12.3) 和第二个标准转换序列组成。

\n
\n

标准转换序列在 \xc2\xa74[conv] 中定义:

\n
\n

[...] 标准转换序列是按以下顺序进行的标准转换序列

\n
    \n
  • 以下集合中的零次或一次转换:左值到右值转换、数组到指针转换以及函数到指针转换。

    \n
  • \n
  • 以下集合中的零个或一个转换:整数提升、浮点提升、整数转换、浮点转换、浮点整数转换、指针转换、指向成员的指针转换和布尔转换。

    \n
  • \n
  • 零或一次资格转换。

    \n
  • \n
\n
\n

问题是您的代码无法通过应用单个用户定义的转换从 a)int右值到 b)点。B

\n

A(int)特别是,所有可用的转换序列都以产生右值的用户定义的转换(隐式构造函数)开始A。从那里开始,右值不能绑定到 call 的非常量引用A::A( A& ),因此该路径被丢弃。所有其他路径都需要第二个用户定义的转换,这是不允许的,事实上,唯一能让我们到达 b) 的其他路径需要另外两个用户定义的转换,总共 3 个。

\n