为什么这个模板类无法编译?

3

我有以下代码:

template <typename Type>
class Delegate
{
public:
    Delegate(Type x)
    {
    }
};

void Method()
{
}

int main()
{
    Delegate d(&Method);
    return 0;
}

我的问题是:为什么编译器不能根据传入构造函数的参数来推导模板类型?我得到的编译错误是:缺少类模板Delegate的参数列表。我明白这一点,但我认为类型推断可以解决这个问题,从而实现更清晰的语法。


我理解这个错误,只是很惊讶构造函数不能使用类型推断,想知道为什么。谢谢。 - Will Custode
抱歉,我错过了最后一句话。已经删除了我的评论 :) - puelo
2个回答

6
由于模板参数推导仅适用于函数。类模板始终需要显式指定参数。
这就是为什么许多模板具有“命名构造函数”,它只是构造临时实例的函数,但由于是函数模板而不是类模板,因此可以推断参数。例如std::make_pair
C++11引入了auto的这种新含义,实际上允许您推断变量的类型。因此,如果您使用C++11,可以为您的类创建“命名构造函数”,如下所示:
template <typename Type>
Delegate<Type> delegate(Type x) { return Delegate<Type>(x); }

你可以使用它创建一个被推断类型的变量,例如:

auto d = delegate(&Method);

请注意,这将推断d的类型与初始化程序返回的类型完全相同(如果您想要auto &auto &&,那么就可以使用,但不要超出此范围)。这比尝试推断假设的Delegate d(&Method)要容易得多,因为这将涉及在构造函数重载和可行构造函数集之间进行重载分辨率的类型推导之间存在循环依赖关系以及可推导类型取决于的情况(请记住,构造函数可以重载,并且类型可以部分特化)。

在尝试了一段时间之后,这就是我想出来的,但我不确定。感谢您的澄清。不过,这种情况有什么特别的原因吗?我的意思是,构造函数可以应用类型推断是有道理的。 - Will Custode
@WilliamCustode:规则可能会相当复杂。好消息是,在C++11中,您可以使用“auto”关键字和适当的命名构造函数来完成它。我已经在答案中进行了扩展。 - Jan Hudec
@WilliamCustode:我尝试解释为什么“Delegate d(&Method)”比“auto”更困难。 - Jan Hudec

1
这是同样的原因,它不起作用:

// attempt to create a std::vector<std::string> of ten "x" strings:
std::vector v(10, "x");

事实上,它应该会产生完全相同的错误信息。
使用以下代码进行类型推导:
template <class Type>
Delegate<Type> MakeDelegate(Type const &x)
{
    return Delegate<Type>(x);
}

或者像使用 std::vector 一样,显式声明类型。

顺便提一下,main 函数必须返回 int,而未知类型的参数(例如在模板中)应该使用 const& 进行传递。


我很感谢关于main和const的附注,但这显然是示例代码。我已经按照您的建议制作了一个创建对象的方法。谢谢! - Will Custode
欢迎您。但是,使用 void main 发布代码有两个很大的缺点:1.) 它会教给阅读您问题的新手错误的东西。2.) 在GCC中,它是一个编译器错误,因此您使人们更难编译您的示例代码。 - Christian Hackl
1
未知类型的参数通常应通过const引用传递,但是_functor_类型的参数惯例上是通过值传递的! - Jan Hudec
@JanHudec:谢谢,这比我的建议更精确。通常迭代器类型也是如此。当然,在任何情况下,这只是一般指导方针,而不是严格规定。 - Christian Hackl
我认为这与所提出的问题完全无关。任何阅读C++问题的人都应该知道main是什么。 - Will Custode
你认为让别人帮助你更容易是无关紧要的吗?当人们查看问题时,复制和粘贴示例代码到编辑器通常是他们做的第一件事。如果您让他们花费更多时间来使您的代码可编译(或使其产生您想了解更多信息的相同编译器消息),他们放弃并寻找下一个有趣的问题的可能性就更大。 - Christian Hackl

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