【问题标题】:Why does C++ prefer this template method to a method overload?为什么 C++ 更喜欢这个模板方法而不是方法重载?
【发布时间】:2012-06-11 12:05:07
【问题描述】:

假设我有两个类,第一个用于编写基本类型(boolintfloat 等),第二个扩展第一个以编写复杂类型:

struct Writer {
    virtual void Write(int value) = 0;
};

struct ComplexWriter : public Writer {
    template <typename TValue> void Write(const TValue &value) {
        boost::any any(value);
        Write(any);
    }
    //virtual void Write(int value) = 0; // see question below
    virtual void Write(const boost::any &any) = 0;
};

这个想法是,如果有人调用myWriter.Write(someIntValue);,int 重载将优先于模板化方法。

相反,我的编译器 (Visual C++ 11.0 RC) 总是选择模板方法。例如,下面的代码 sn -p 会将Wrote any 打印到控制台:

struct ComplexWriterImpl : public ComplexWriter {
    virtual void Write(int value) { std::cout << "Wrote an int"; }
    virtual void Write(const boost::any &any) { std::cout << "Wrote any"; }
};

void TestWriter(ComplexWriter &writer) {
    int x = 0;
    writer.Write(x);
}

int main() {
    ComplexWriterImpl writer;
    TestWriter(writer);
}

当我也在ComplexWriter 类中声明Write(int) 方法时,行为突然发生了变化(参见第一个sn-p 中的注释行)。然后它将Wrote an int 打印到控制台。

这是我的编译器应该表现的方式吗? C++ 标准是否明确规定只有在同一类(而不是基类)中定义的重载才能优先于模板化方法?

【问题讨论】:

  • 我没有看到你在哪里重载了一个采用模板化的 int 方法。您有两个不相关的类,Writer 带有一个采用 int 的方法和 ComplexWriter 带有一个模板化方法(以及一个采用 boost::any const&amp; 的方法)。在您的测试代码中,您使用ComplexWriter,所以它当然会调用ComplexWriter 的成员。如果你真的希望它调用完全不相关类的成员Writer,你能解释一下为什么(以及如何)它应该这样做吗?
  • 您提到ComplexWriter 扩展了Writer,但在您列出的代码中,它不继承自Writer。这是正确的设置吗?
  • 感谢您指出这一点。是的,ComplexWriter 应该派生自 Writer。我相应地更新了问题。结果仍然是一样的(尽管当然忘记指定基类连接到我的问题的核心。outch。)。

标签: c++ templates boost overloading


【解决方案1】:

问题在于,在您调用writer.Write(x) 时,编译器看到的是ComplexWriter 而不是ComplexWriterImpl,因此它只知道ComplexWriter 中定义的函数——模板函数和@ 987654327@ 功能。

ComplexWriter 不包含任何接受int 的虚函数,因此无法调用ComplexWriterImpl 中定义的 int 重载

当您将虚拟重载添加到 ComplexWriter 类时,编译器会意识到 ComplexWriter 类中有一个整数重载,因此会调用它在 ComplexWriterImpl 中的实现

编辑:既然您已经在 ComplexWriter 和 Writer 之间的继承中进行了编辑,我有一个更完整的解释给您:

当您创建一个子类并在其中定义一个函数时,基类中该名称的所有函数都将被隐藏,无论其参数类型如何。

我相信你可以使用 using 关键字来解决这个问题:

struct ComplexWriter : public Writer {
    template <typename TValue> void Write(const TValue &value) {
        boost::any any(value);
        Write(any);
    }
    using Writer::Write;
    virtual void Write(const boost::any &any) = 0;
};

有关更多详细信息,请参阅此常见问题解答条目:http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9

编辑 2:只是为了确认这确实解决了您的问题:http://ideone.com/LRb5a

【讨论】:

  • 是的,我忘了在我的 sn-p 中指定基类。 ComplexWriter 确实继承自 Writer,但我观察到的行为正如我所解释的那样,即使正确指定了基类。
  • @Cygon 是的,注意到了。我现在只是想提出一个解释/编辑。虽然如果我失败了,我可能最终会删除这个答案!
  • 无论如何感谢您的快速回复!忘记指定基类确实是一个愚蠢的错误,因为它歪曲了整个问题:)
  • 不错!我已经编写 C++ 好几年了,以前从未遇到过这种类型的 using 语句。即使有多个重载,它也会在我的实际 GCC 和 MSVC 上产生预期的结果。
【解决方案2】:

当您通过ComplexWriter“接口”访问对象时,编译器将尝试使用该类中的定义解析对Write(int) 的函数调用。如果它不能这样做,它将考虑基类。

在这种情况下,您有两个候选者:Write(any) 和模板版本。由于此时没有明确的Write(int) 可用,因此必须在这两个选项之间进行选择。 Write(any) 需要隐式转换,而模板化版本不需要,因此调用模板化版本(进而调用Write(any))。

要使Writer 中的Write(int) 可用,请导入Writer::Write 函数:

class ComplexWriter : public Writer
{
  using Writer::Write;
  // rest is as before
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-27
    • 2015-08-13
    • 1970-01-01
    相关资源
    最近更新 更多