c++ - 如何在模板化转换运算符中消除这种构造的歧义?

标签 c++ templates type-conversion ambiguous

在对为什么我的代码在 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, with T 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 of S 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-qualified T” 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,即我们的 copymove 构造函数。这整个行为与 [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/

相关文章:

python - 如何将 JSON 数据从 Django View 传递到 Vue.js 实例方法

sqlite - 我应该如何处理 SQLalchemy 和 SQLite 中的小数

c++ - 将 unsigned char 转换为 unsigned short (big endian)

r - 给定一个字符串列表,如何转换回数字列表?

C++ 在 if 语句中使用 char

C++ 计算字符串中的字符数

c++ - 在模板函数中包含不变假设

c++ - 将文件中的数据分配给矩阵 C++

c++ - 如何在文本文件中搜索单词,如果找到则打印出整行

templates - 恢复混合页面 Alfresco Share