为什么在传递给参数时不能隐式构造给定合适构造函数的对象?

Why can't I implicitly construct an object given a suitable constructor when passing to an argument?

本文关键字:构造函数 对象 参数 不能 为什么      更新时间:2023-10-16

在下面的示例中,为什么我不能简单地将string传递给printFoo()

#include <string>
#include <iostream>
using namespace std;
class Foo {
public:
  Foo(const Foo &foo) : str(foo.str) {}
  Foo(string str) : str(str) {}
  string str;
};
void printFoo(Foo foo) {
  cout << foo.str << endl;
}
int main() {
  Foo foo("qux");
  printFoo(foo); // OK
  printFoo("qix"); // error: no matching function for call to 'printFoo'
  return 0;
}

无论出于何种原因,我脑海中都有一个构造函数会自动确定并使用,以便构造一个对象。

为什么我不能这样做,但我可以将一个char[n]常量传递给接受std::string的参数,例如?

将涉及两个隐式转换:

  1. std::string
  2. Foo

C++最多执行一个:

从 4 标准转换 (N3337)

标准转换是具有内置含义的隐式转换。 第4条列举了此类转换的全部集合。一个标准 转换序列是标准转换序列 以下顺序:

— 以下集合中的零个或一个转换: 左值到重值转换、数组到指针转换以及 函数到指针的转换。

— 零或一次转换从 以下集合:积分促销、浮点促销、积分 转换、浮点转换、浮点积分 转换、指针转换、指向成员的指针转换,以及 布尔转换。

— 零或一次资格转换。

还有 12.3 转换 (N3337)

1 类对象的类型转换可以由构造函数和 通过转换函数。这些转换称为用户定义 转换,用于隐式类型转换(条款 4),用于 初始化 (8.5) 和显式类型转换 (5.4、5.2.9)。

2 用户定义的转换仅在明确的情况下应用 (10.2, 12.3.2).转换遵守访问控制规则(第 11 条)。 访问控制在歧义解决 (3.4) 之后应用。

[...]

4 最多一个用户定义隐式应用转换(构造函数或转换函数)到单个值。

(强调我的)

根据C++标准 §12.3/4 转换 [class.conv]:

最多一个用户定义的转换(构造函数或转换) 函数)隐式应用于单个值。

因此,不允许编译器连续应用两个转换。也就是说,首先从const char[4]std::string,其次从std::stringFoo.

为此,您需要定义一个额外的构造函数:

Foo(char const *str_) : str(str_) {}

这是因为编译器可以考虑一次转换。

要执行您需要执行的操作,编译器需要植入两个转换。

printFoo("qix");
// Actually needs.
printFoo(Foo(std::string("qix")));

如果您更改此设置以传递字符串,它将起作用。

printFoo(std::string("qix"));

这一切背后的主要原因是字符串文字的类型char const[<size>]std::string

正如其他人所提到的,问题是需要 2 次转换。您可以使用s文本将字符串文本转换为实际std::string

printFoo("qix"s);

演示