在对为什么我的代码在 GCC 上给我一个模棱两可的错误而在 Clang 上没有错误感到困惑之后,我简化了代码。如下图所示。
struct Foo
{
// Foo(Foo&&) = delete;
// Foo(const Foo&) = delete;
Foo(int*) {}
};
struct Bar
{
template<typename T>
operator T()
{
return Foo{nullptr};
}
};
int main() { Foo f{Bar{}}; }
错误如下。
main.cpp:17:18: error: call to constructor of 'Foo' is ambiguous
int main() { Foo f{Bar{}}; }
^~~~~~~~
main.cpp:1:8: note: candidate is the implicit move constructor
struct Foo
^
main.cpp:1:8: note: candidate is the implicit copy constructor
main.cpp:5:1: note: candidate constructor
Foo(int*) {}
^
这次我无法为 Clang 成功编译,所以我想这只是一个 Clang 错误,这是预期的行为。
当我显式删除复制和移动构造函数(即取消注释前两行代码)时,我得到
note: candidate constructor has been explicitly deleted
但仍然是一个错误。那么,我将如何消除这里的构造歧义呢?
请注意,我专门添加了 Foo{nullptr}
而不仅仅是 nullptr
,但没有区别。与显式标记 Foo
ctor 相同。仅当 Bar
的转换运算符被模板化时,才会出现此歧义错误。
我可以将一些 SFINAE 添加到转换运算符,但我不确定我会排除什么。例如,这将使它工作:
template<typename T, std::enable_if_t<std::is_same<T, Foo>{}>* = nullptr>
这是我找到的另一个,这可能是我的答案:
template<typename T, std::enable_if_t<!std::is_same<T, int*>{}>* = nullptr>
最佳答案
要解决歧义,请添加 explicit
到转换运算符声明:
struct Bar
{
template<typename T>
explicit operator T()
{
return Foo{nullptr};
}
};
为什么有必要?因为Foo
有一个构造函数采用 int*
, 所以一个 operator int*()
operator T()
的实例化模板 被视为 f
初始化的重载解决方案的一部分.见下[over.match.copy] :
1 [...] Assuming that “
cv1 T
” is the type of the object being initialized, withT
a class type, the candidate functions are selected as follows:
(1.1) The converting constructors of
T
are candidate functions.(1.2) When the type of the initializer expression is a class type “
cv S
”, the non-explicit conversion functions ofS
and its base classes are considered. When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualifiedT
” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T
”, explicit conversion functions are also considered.
从 (1.2) 可以看出,初始化只考虑隐式转换函数,因此存在歧义——因为编译器无法在构造 f
之间做出决定使用对 Foo
的引用或者,如前所述,使用 int*
(通过复制初始化的方式获得)从operator int*
的返回值. 然而,当初始化表达式是一个临时对象时,我们也会考虑显式转换——但前提是它们匹配引用 Foo
的构造函数,我们的“可能是 cv 限定的 T
”,即我们的 copy 和 move 构造函数。这整个行为与 [class.conv.fct¶2] 一致。 :
A conversion function may be explicit ([dcl.fct.spec]), in which case it is only considered as a user-defined conversion for direct-initialization ([dcl.init]). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.
所以,第三次在这里说同样的话:如果它没有被标记为 explicit
,没有什么能阻止编译器尝试复制初始化 int*
用于建筑。
关于c++ - 如何在模板化转换运算符中消除这种构造的歧义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51972738/