使用字符串字面量构建一个对象。

8

我有以下的类:

#include <iostream>
#include <string>

using namespace std;

class CLS
{
   int value;
   string str_value;

public:

    CLS(int param) { value = param; }

    CLS(string param)
    {
       str_value = param;
    }
};

int main()
{
    CLS a(2);
    CLS b = 3;
    CLS c("4");
    CLS d = "5";  // Error: invalid conversion from 'const char*' to 'int'
}

我搜索了错误的原因,但没有成功。
使用字符串字面量构建是否正确? 如果不是,为什么? 如果是,我的代码有什么问题?
我正在使用gcc 5.3和Code::Blocks 16.1。

5
我注意到这个在VS2017下编译非常顺利。值得记住的是,字符串字面值并不等同于std::string - Rook
这是一个完整的示例吗?是否包含任何头文件?在你的情况下,string是什么? - ArturFH
@Artur R. Czechowski - 这是一个完整的示例(我添加了头文件)。我猜string应该是std::string - Kamal Zidan
1
尝试为const char*添加一个构造函数。正如@Artur R. Czechowski所说,std::string与字符串字面值不同。 - Gambit
2
@Rook MSVC有一个扩展,允许多个隐式用户定义的转换。将警告级别提高应该会产生诊断信息。 - Quentin
@Quentin 很有趣,谢谢。 - Rook
2个回答

11
起初,"4" 不是 std::string,而是 const char[2]
然后,CLS c("4");直接初始化,将检查CLS的构造函数以初始化c。在这里选择了CLS::CLS(string),因为const char[]可以通过用户定义的转换(即通过std::string::string(const char*))隐式转换为std::stringCLS d = "5";复制初始化
(强调我的)
  • 如果T是类类型,并且other的类型的cv-unqualified版本不是T或派生自T,或者T是非类类型,但other的类型是类类型,则将考虑可从other的类型转换为T的用户定义转换序列(如果T是类类型并且有一个转换函数可用,则转换为派生类型),并通过重载分辨率选择最佳转换。
这意味着需要用户定义的转换序列来将const char[2]转换为CLS。即使const char[]可以转换为std::string,并且std::string可以转换为CLS,但在一个隐式转换序列中只允许一个用户定义的转换。这就是为什么它被拒绝的原因。
(强调我的)

隐式转换序列按以下顺序组成:

1)零或一个标准转换序列;
    2)零或一个用户定义的转换;
    3)零或一个标准转换序列。

顺便说一句:如果您将其更改为显式使用std::string作为初始化表达式,则可以正常工作。例如:
CLS d = std::string{"5"};  // pass a temporary std::string constructed from "5" to the constructor of CLS

using namespace std::string_literals;
CLS d = "5"s;              // "5"s is a string literal of type std::string (supported from C++14)

那么一个用户定义的转换需求也适用于隐式转换吗?否则 const char[2] -> 隐式 const char* -> 用户定义的 std::string,对吧? - Rakete1111
@Rakete1111(1)是的,在一次隐式转换序列中只允许一个用户定义的转换;(2)我不理解你的意图,“const char [2]” -> “const char*” 不是用户定义的转换,而是标准转换,可以多次执行。 - songyuanyao
是的,这正是我的观点:如果只允许一个用户定义的转换,那么const char [2] -> const char *不算,可以使用std::string的构造函数(这是1个用户定义的转换)。但是为什么不允许呢? - Rakete1111
@Rakete1111 这是因为我们需要将 const char[2] 转换为 CLS 以进行复制初始化。 - songyuanyao
哦,所以不允许中间转换? - Rakete1111
1
@Rakete1111 是的,否则用户定义的转换就可以被链接。 - songyuanyao

8
CLS a(2); 
CLS b = 3;
CLS c("4");
CLS d = "5";

ac使用直接初始化进行初始化。bd则使用复制初始化

区别在于,对于复制初始化,编译器会搜索从(在d的情况下)char const *CLS的(单个)用户定义转换,而对于直接初始化,将尝试所有构造函数,其中可以使用CLS(std::string),因为有一个可用的转换std::string(char const*)

细节:

"5"是一个(C)字符串字面值。它的类型是char const [2]。因此,首先搜索从该类型到CLS的用户定义转换。由于找不到,应用从Type[N]Type *(其中Type=char constN=2)的标准转换,结果为char const *。然后编译器尝试找到从该类型到CLS的用户定义转换。由于找不到,并且没有更多可用的标准转换可供尝试,因此编译失败。"

你的意思是"[...]用户自定义转换(在d的情况下)从char const[][...]"?"5"可以衰减为const char*,但它是一个const char[2] - Rakete1111
@Rakete1111 在这里,我最初没有在答案中涉及衰减,以使重点集中在转换上。 - Daniel Jour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接