为什么这种移动和值构造函数的组合对于 MSVC 不明确,但对于 C++17 及更高版本中的 Clang 和 GCC 却不明确
Why is this combination of move and value constructor ambigous for MSVC but not for Clang and GCC in C++17 and above
我有以下代码
struct R {
int i_ = 8;
R() = default;
R(R const& o) = default;
R(R&& o) = default;
R(int i) : i_(i) {}
operator int() const { return i_; }
};
struct S {
R i_;
operator R() const { return i_; }
operator int() const { return static_cast<int>(i_); }
};
int main() {
S s;
R r0(s);
R r = static_cast<R>(s);
float af[] = {1,2,3};
float f1 = af[s];
float f2 = af[r];
}
在 C++17 和 C++20(不适用于 C++ < 17)的 Clang 和 GCC 上编译良好,但抱怨
'R::R': ambiguous call to overloaded function
note: could be 'R::R(R &&)'
note: or 'R::R(int)'
在 MSVC 中适用于所有可用标准。
我试图通过比较 MSVC MSVC, Clang and GCC and also tried /permissive- 来找出语言一致性的差异,但还找不到任何解释。
- 谁是对的(为什么)
- 如何在不放弃 R 中的
R&&
和 int i
构造函数或显式标记转换 operator int()
的情况下为 MSVC 进行编译?
这里 a rather crowded godbolt 包括一个可能的解决方法。
这是 CWG 2327,它目前没有解决方案,但 gcc 和 clang 似乎在做“正确的事情”。问题内容如下:
Consider an example like:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
This goes to 9.4 [dcl.init] bullet 17.6.2:
Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one is chosen through overload resolution (12.2 [over.match]). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
重载决议选择Cat
的移动构造函数。根据 9.4.4 [dcl.init.ref] 项目符号 5.2.1.2,初始化构造函数的 Cat&&
参数会产生一个临时值。这排除了这种情况下复制省略的可能性。
这似乎是对保证复制省略的措辞更改的疏忽。在这种情况下,我们大概应该同时考虑构造函数和转换函数,就像我们对复制初始化所做的那样,但我们需要确保这不会引入任何新问题或歧义。
这里的情况类似,只是更复杂。有:
R r0(s);
R r = static_cast<R>(s);
我们有一个来自 s
的转换函数,它给出了一个 prvalue R
,这是一个比我们可以使用的任何其他路径都更好的路径——无论是通过 R
的移动构造函数或R(int)
。但我们只是没有规定那是我们应该做的。
gcc 和 clang 似乎在 C++17 或更高版本(我们保证复制省略)上实现了所需的行为,尽管没有任何关于所需行为的措辞。另一方面,msvc 似乎遵循指定的规则(我认为确实将这种情况指定为模棱两可)。
我有以下代码
struct R {
int i_ = 8;
R() = default;
R(R const& o) = default;
R(R&& o) = default;
R(int i) : i_(i) {}
operator int() const { return i_; }
};
struct S {
R i_;
operator R() const { return i_; }
operator int() const { return static_cast<int>(i_); }
};
int main() {
S s;
R r0(s);
R r = static_cast<R>(s);
float af[] = {1,2,3};
float f1 = af[s];
float f2 = af[r];
}
在 C++17 和 C++20(不适用于 C++ < 17)的 Clang 和 GCC 上编译良好,但抱怨
'R::R': ambiguous call to overloaded function
note: could be 'R::R(R &&)'
note: or 'R::R(int)'
在 MSVC 中适用于所有可用标准。
我试图通过比较 MSVC MSVC, Clang and GCC and also tried /permissive- 来找出语言一致性的差异,但还找不到任何解释。
- 谁是对的(为什么)
- 如何在不放弃 R 中的
R&&
和int i
构造函数或显式标记转换operator int()
的情况下为 MSVC 进行编译?
这里 a rather crowded godbolt 包括一个可能的解决方法。
这是 CWG 2327,它目前没有解决方案,但 gcc 和 clang 似乎在做“正确的事情”。问题内容如下:
Consider an example like:
struct Cat {}; struct Dog { operator Cat(); }; Dog d; Cat c(d);
This goes to 9.4 [dcl.init] bullet 17.6.2:
Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (12.2.2.4 [over.match.ctor]), and the best one is chosen through overload resolution (12.2 [over.match]). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
重载决议选择
Cat
的移动构造函数。根据 9.4.4 [dcl.init.ref] 项目符号 5.2.1.2,初始化构造函数的Cat&&
参数会产生一个临时值。这排除了这种情况下复制省略的可能性。这似乎是对保证复制省略的措辞更改的疏忽。在这种情况下,我们大概应该同时考虑构造函数和转换函数,就像我们对复制初始化所做的那样,但我们需要确保这不会引入任何新问题或歧义。
这里的情况类似,只是更复杂。有:
R r0(s);
R r = static_cast<R>(s);
我们有一个来自 s
的转换函数,它给出了一个 prvalue R
,这是一个比我们可以使用的任何其他路径都更好的路径——无论是通过 R
的移动构造函数或R(int)
。但我们只是没有规定那是我们应该做的。
gcc 和 clang 似乎在 C++17 或更高版本(我们保证复制省略)上实现了所需的行为,尽管没有任何关于所需行为的措辞。另一方面,msvc 似乎遵循指定的规则(我认为确实将这种情况指定为模棱两可)。