【问题标题】:Why can't the template argument be deduced when it is used as template parameter to another template?为什么当模板参数用作另一个模板的模板参数时,不能推导出模板参数?
【发布时间】:2011-08-28 23:36:50
【问题描述】:

这段代码有什么问题?

#include <map>

template<typename T>
struct TMap
{
    typedef std::map<T, T> Type;
};

template<typename T>
T test(typename TMap <T>::Type &tmap_) { return 0.0; }

int _tmain(int argc, _TCHAR* argv[])
{
    TMap<double>::Type tmap;
    tmap[1.1] = 5.2;
    double d = test(tmap); //Error: could not deduce template argument for T
    return 0;
}

【问题讨论】:

    标签: c++ templates parameters


    【解决方案1】:

    这是不可演绎的上下文。这就是为什么编译器不能推导出模板参数的原因。

    试想一下,如果你有专门的TMap 如下:

    template <>
    struct TMap<SomeType>
    {
        typedef std::map <double, double> Type;
    };
    

    假设TMap&lt;SomeType&gt;::Typestd::map&lt;double, double&gt;,编译器将如何推断SomeType 类型?这不可以。 保证您在std::map 中使用的类型也是TMap 中的类型。编译器不能做出这种危险的假设。 type 参数之间可能没有任何关系。

    另外,您可能将TMap 的另一个特化定义为:

    template <>
    struct TMap<OtherType>
    {
        typedef std::map <double, double> Type;
    };
    

    这使情况变得更糟。现在您有以下内容:

    • TMap&lt;SomeType&gt;::Type = std::map&lt;double, double&gt;
    • TMap&lt;OtherType&gt;::Type = std::map&lt;double, double&gt;

    现在问问自己:给定TMap&lt;T&gt;::Typestd::map&lt;double, double&gt;,编译器如何知道TSomeType 还是OtherType?它甚至不知道它有多少个这样的选择,它也不知道选择本身......

    我只是为了思想实验而问你(假设它可以知道完整的选择集)。

    【讨论】:

    • 绝妙的答案。这个问题一再让我绊倒,我花了很长时间才明白为什么不可能推断出上下文。您的简短示例完美地说明了这一点。
    • 所以我必须写:double d = test(tmap) ?
    • 如果我能给这个答案+10。与主题的复杂性相比,答案如此彻底和准确。
    • 这本质上是一个反函数问题:给定一个输出值,您要求输入函数获取该值的输入点。但是,函数通常不可逆,因此除非您要求函数是可逆的,否则这个问题甚至不存在。在模板场景中,相应的要求是所有模板都具有全局唯一的成员名称; C++ 没有提出的要求。
    • @segfolt:不,这并不明显。请多读几遍我的回答,并阅读有关模板专业化的内容。
    【解决方案2】:

    正是编译器错误消息所说的:in TMap&lt;T&gt;::Type, T 不能根据 标准。这样做的动机可能是它不是 技术上可能实现:编译器必须 实例化所有可能的TMap&lt;T&gt; 以查看是否有一个(和 只有一个)匹配您传递的类型。并且有一个 无限数量的TMap&lt;T&gt;

    【讨论】:

    • 我认为你在这里犯了一个错误——编译器“仅”必须考虑所有现有特化的传递闭包并推断出最佳匹配。这是有限(但可能仍然非常大)数量的实例化。但除此之外,可能还有几个同样出色的匹配项(参见 Nawaz 的示例)。
    • 没有。在许多情况下,不会有任何现有的专业化。即使有,类型归纳也可以触发新的专业化。
    • @James 如果没有特化,那么情况很“容易”:只需尝试对 typedef 进行模式匹配来推断模板参数。这在原则上应该是可行的。
    • @Konrad:即使没有专精,情况也不容易。假设std::map 的类型参数相同或以某种方式取决于TMap 的类型是不安全的。因此它是不可行的。
    • 最糟糕的问题当然是模板元编程是图灵完备的。这反过来意味着必要的模式匹配等同于解决停止问题(即编写 A&lt;N&gt;::B 的特化,以便将 B 类型定义为 int iff N 表示停止的算法)
    【解决方案3】:

    即使你有:

    TMap<SomeType>::Type = std::map<double, double>. 
    

    但在调用 test(tmap) 之前

    TMap<double>::Type tmap;
    tmap[1.1] = 5.2;
    double d = test(tmap); 
    

    你已经把它声明为

    TMap<double>::Type tmap;
    

    为什么不能使用此信息。 #typedef 不仅仅是简单的字符串替换。

    【讨论】:

      【解决方案4】:

      我不认为“我们不能这样做”的说法是正确的。 如果我们稍微修改这个例子,编译器就会很高兴地为我们推导参数。

      template<typename T>
      struct TMap //...
      
      template <class T>
      struct tmap_t : TMap<T>::Type {};
      
      template<typename T>
      T test(tmap_t<T> tmap) // ...
      
      tmap_t<double> tmap;  // ...
      double d = test(tmap);  // compiles just fine.
      

      我认为原始示例与我的示例之间没有太大区别。这里真正的问题似乎是 C++ 对待 typedef 和类型声明的方式不同

      这是好事吗?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-01
        • 2018-10-25
        • 2018-12-03
        • 2019-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多