【问题标题】:Compiler chooses wrong template function specialization编译器选择了错误的模板函数特化
【发布时间】:2011-06-21 04:13:04
【问题描述】:

我正在拼命地尝试让我的专长发挥作用,但由于推断出不正确的参数,我仍然有不可编译的代码。请注意,错误不是关于定义模板,而是关于在错误的模板实现中应用不相关的操作。演示问题的代码的简化示例是:

struct Test { void Method() const {} };

template<typename T>
void Cmp(T _val) { _val > 1; }

template<>
void Cmp<const Test &>(const Test &_val) { _val.Method(); }

template<>
void Cmp<const char *>(const char *_val) { _val[2]; }

int main()
{
  Test test1;
  char test2[5];

  Cmp(10);    // ok, expected
  Cmp(test1); // error in Cmp(T)?! but expecting to instantiate Cmp(const Test &)
  Cmp(test2); // error in Cmp(T)?! but expecting to instantiate Cmp(const char *)
  return 0;
}

我真的不希望使用像 Cmp&lt;const Test &amp;&gt;(test1) 这样的显式调用(有效),因为 AFAIK 编译器应该能够自动推断参数,并且这些专业化背后的整个想法是透明地调度 Cmp 调用(在实际代码中,我正在定义运算符)。当然,按值特化 Cmp&lt;Test&gt;(Test) 按预期工作,但对于大型复杂的非 POD 类,按值传递它是荒谬的。

无论我尝试应用什么修复程序,编译器都会顽固地拒绝使用通用模板来选择按引用专业化。似乎我遗漏了一些重要的东西,但我真的很想知道为什么我的方法不起作用,以及我应该如何构造代码来表达来自非模板化 C++ 世界的这样一个简单概念的代码,作为通过引用传递类。当然,事实证明谷歌对这个问题完全没用。 ^_^ 我尝试了 GCC 4.2.1 和 4.4.6。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:
    Cmp(test1);
    

    这里,T 从参数推导出为Test。它没有被推断为const Test&amp;,因此您的专业化不匹配,因此为什么要实例化主模板。您需要使您的专业化按价值获取Test。有了这个声明,确实使用了特化:

    template<>
    void Cmp<Test>(Test _val) { _val.Method(); }
    

    Cmp(test1);
    

    这里,T 被推导出为char*,而不是const char*,因此专业化不匹配。您需要专门针对 char* 而不是 const char* 来匹配它。或者,您可以在调用时将参数转换为const char*

    const char* test2ptr = test2;
    Cmp(test2ptr); 
    

    说了这么多,既然可以超载,为什么还要专精呢?

    template<typename T>
    void Cmp(T _val) { _val > 1; }
    
    void Cmp(const Test &_val) { _val.Method(); }
    
    void Cmp(const char *_val) { _val[2]; }
    
    // Add an overload to support arrays of char:
    template <unsigned N>
    void Cmp(const char (&_val)[N]) { _val[1]; }
    

    You really should avoid specializing function templates.这很困难,而且在大多数情况下不值得麻烦。

    【讨论】:

      【解决方案2】:

      嗯……为什么要专攻?只是超载它..

      void Cmp(const Test &_val) { _val.Method(); }
      
      void Cmp(const char *_val) { _val[2]; }
      

      100% 保证选择这些方法。

      【讨论】:

      • 真的...我需要考虑一下为什么我首先要专攻... ^_^ 但是,第二个错误仍然存​​在。编译器仍然选择Cmp&lt;T&gt;(T) 而不是重载的Cmp(const char *)
      • 我可以给@Xeo 和@JamesMcNellis 一个专业化的理由,这叫做可扩展性。如果不是为了模板专业化,您将如何为您的自定义类重载operator &lt;&lt;(提取运算符)?我在数学库中使用专业化,我必须允许其他类在不编辑 Identity&lt;&gt; 类的情况下定义它们的加法和乘法恒等式。我之所以发表此评论,是因为两个答案都问“为什么要专门化”,导致 OP 怀疑专业化。
      • @MarkMYoung:您可以通过比函数模板专业化更好的方法来实现可扩展性。 (FWIW,这里没有人在谈论 class 模板专业化,这很好。)
      猜你喜欢
      • 2011-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多