为什么在使用转换操作符时编译器无法推导模板参数?

15

请看以下代码:

#include <utility>

template<typename T>
struct wrapper {
    T value;
};

struct foo {
    operator wrapper<int>() {
        return{10};
    }
};


int main() {
    foo f;
    wrapper w = f; // error
    std::pair p = std::make_pair(1, 0); // ok
}

gcc 7.1.1在上面标记的行处无法编译:

main.cpp: In function 'int main()':
main.cpp:17:17: error: class template argument deduction failed:
     wrapper w = f; // error
                 ^
main.cpp:17:17: error: no matching function for call to 'wrapper(foo&)'
main.cpp:4:8: note: candidate: template<class T> wrapper(wrapper<T>)-> wrapper<T>
 struct wrapper {
        ^~~~~~~
main.cpp:4:8: note:   template argument deduction/substitution failed:
main.cpp:17:17: note:   'foo' is not derived from 'wrapper<T>'
     wrapper w = f; // error
                 ^

f 可以转换为 wrapper<int>,所以我期望这样做。从那里编译器应该能够推断出 Tint。但是它不能。

编译器可以正确推断出 std::pair 的模板参数,所以我想知道为什么对于 wrapper 来说不是这种情况。

有什么想法吗?


在我的看法中,如果类模板推导考虑了转换运算符,这会令人感到困惑。如果需要这样做,应该使用显式的推导指南。 - cpplearner
相关链接:https://stackoverflow.com/q/43019240/1896169 - Justin
这个 operator struct wrapper <int> () 是什么?它声明了 operator wrapper 还是 operator () - cat
@cat 这是一个转换运算符。因此,技术上是一个“操作符包装器”。 - Rakete1111
1个回答

11
对于类模板参数推导,"重载集"由[over.match.class.deduct/1]所述组成。它们如下:
引入一组函数和函数模板: (1.1) - 对于模板名称指定的主类模板的每个构造函数(如果定义了模板),都会生成一个函数模板,该函数模板具有以下属性: 1.1.1 模板参数是类模板的模板参数,后面跟着构造函数的模板参数(包括默认模板参数),如果有的话。 1.1.2 函数参数的类型是构造函数的类型。 1.1.3 返回类型是从类模板获得的模板参数对应于模板名称和模板参数指定的类模板特化。
(1.2) - 如果主类模板C未定义或未声明任何构造函数,则从假想构造函数C()派生出另一个函数模板。
(1.3) - 从名为C(C)的假想构造函数派生出另一个函数模板,称为副本推导候选函数。
(1.4) - 对于每个推导指南,具有以下属性的函数或函数模板: 1.4.1 如果有,则模板参数和函数参数是推导指南的参数。 1.4.2 返回类型是推导指南的简单模板ID。
正如您所看到的,在1.1中匹配的"函数"仅尝试将参数类型与模板参数类型完全匹配,它不考虑转换(就像大多数其他模板推导相关的行为一样)。
之所以它适用于std::pair,是因为1.3中的条款和它定义的“复制推导候选函数”。

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