首页 > 解决方案 > 为什么这种复制初始化(带有两个隐式转换)在 GCC/Clang 中不起作用?

问题描述

请考虑以下代码:

#include <string.h>

class cstring {
public:
    cstring(const char* str) : begin_(str), end_(str ? str + strlen(str) : 0)
    {
    }

    size_t size() const
    {
        return (size_t)(end_ - begin_);
    }

    const char* data() const
    {
        return begin_;
    }

private:
    const char* begin_;
    const char* end_;
};

class Name {
public:
    Name(cstring str)
    {
    }
};

int main()
{
    cstring str = "Hello World"; // OK

    Name name = str; // OK

    Name name2 = "James"; // error
}

Clang 会输出错误(GCC 也有类似的输出):

Source.cpp:37:10: error: no viable conversion from 'const char [6]' to 'Name'
    Name name2 = "James"; // error
         ^       ~~~~~~~
Source.cpp:24:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
      'const char [6]' to 'const Name &' for 1st argument
class Name {
      ^
Source.cpp:26:5: note: candidate constructor not viable: no known conversion from 'const char [6]' to 'cstring' for 1st
      argument
    Name(cstring str)
    ^
1 error generated.

上述代码中,由于构造函数不显式,const char*可以隐式转换为cstringcstring也可以隐式转换Name为 ;const char*因此存在从到的转换序列Name。那么为什么 Clang/GCC 不允许这样的复制初始化(msvc 允许这种情况)?

标签: c++implicit-conversioncopy-initialization

解决方案


gcc 和 clang 是正确的,隐式转换序列中只允许进行一次用户定义的转换。

隐式转换序列包含以下内容,按此顺序:

  1. 零个或一个标准转换序列;

  2. 零次或一次用户定义的转换;

  3. 零个或一个标准转换序列。

您可以将复制初始化更改为直接初始化as Name name2 ("James");,其中只需要一次用户定义的转换(从const char*to cstring),并且转换cstring后的作为参数传递给 to 的构造Name函数name2

此外,复制初始化中的隐式转换必须直接从初始值设定项生成 T,而直接初始化需要从初始值设定项到 T 构造函数的参数的隐式转换。


推荐阅读