【问题标题】:With templates: Operator resolved first or conversion resolved first?使用模板:先解析运算符还是先解析转换?
【发布时间】:2011-04-28 17:05:40
【问题描述】:

我昨天看到了一些有趣的编译器行为,我想我理解它为什么会发生,但我想确定一下。所以,我不打算写我的推理,只写事实。

请注意,我使用vector 而不是string 并不是拼写错误。我故意这样做是为了让编译器无法理解 std::string 是什么,因此它必须四处搜索以找出+ 指的是哪个运算符:

#include <vector>
// #include <string> // intentionally commented out

template <typename T> struct A
{
    A() { };
    ~A() { };

    int m_member;
};

template <typename T> A<T> operator+(double lhs, const A<T> &rhs);

int main(int argc, char **argv)
{
    std::string fullString = std::string("Hi ") + std::string("mom!");
}

所以,我在 MS Visual Studio 2005 中遇到了大量编译器错误。我只展示了其中的一部分。

1>.\test.cpp(21) : error C2784: 'A<T> operator +(double,const A<T> &)' : could not deduce template argument for 'const A<T> &' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        .\test.cpp(16) : see declaration of 'operator +'

...错误继续...

1>.\test.cpp(21) : error C2784: 'std::_Vb_iterator<_MycontTy> std::operator +(_Vb_iterator<_MycontTy>::difference_type,std::_Vb_iterator<_MycontTy>)' : could not deduce template argument for 'std::_Vb_iterator<_MycontTy>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(1800) : see declaration of 'std::operator +'

...错误继续...

1>.\test.cpp(21) : error C2784: 'std::_Vb_const_iterator<_MycontTy> std::operator +(_Vb_const_iterator<_MycontTy>::difference_type,std::_Vb_const_iterator<_MycontTy>)' : could not deduce template argument for 'std::_Vb_const_iterator<_MycontTy>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(1695) : see declaration of 'std::operator +'

...错误继续...

1>.\test.cpp(21) : error C2784: 'std::_Vector_iterator<_Ty,_Alloc> std::operator +(_Vector_iterator<_Ty,_Alloc>::difference_type,std::_Vector_iterator<_Ty,_Alloc>)' : could not deduce template argument for 'std::_Vector_iterator<_Ty,_Alloc>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(396) : see declaration of 'std::operator +'

...错误继续...

1>.\test.cpp(21) : error C2784: 'std::_Vector_const_iterator<_Ty,_Alloc> std::operator +(_Vector_const_iterator<_Ty,_Alloc>::difference_type,std::_Vector_const_iterator<_Ty,_Alloc>)' : could not deduce template argument for 'std::_Vector_const_iterator<_Ty,_Alloc>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(264) : see declaration of 'std::operator +'

因此,编译器搜索+ 的含义并抱怨它无法推断出适当的模板参数。我对两个相关的事情感到惊讶,一个是它为每个涉及模板的重载+ 运算符提供此错误。这告诉我编译器绝对无法排除这些+ 中的任何一个都没有意义; 两个,这是相关的,它不只是抱怨没有合适的运营商存在。

我认为这是一个了解如何实例化和编译模板的机会。有人有什么好的解释或参考吗?

【问题讨论】:

  • 哦,只是评论一下为什么编译器甚至知道std::basic_string&lt;...&gt;。它包含在&lt;vector&gt; 通过&lt;xfunctional&gt; 通过&lt;xstring&gt;。 :)
  • +1 这很有趣。我也想知道。

标签: c++ templates compiler-construction compiler-errors


【解决方案1】:

事实上,由于它找不到匹配项,它使用错误来告诉您它实际找到的 可能 运算符候选者。

例如,g++ 错误告诉您它找不到运算符,然后提供相同的大量消息,但以“候选人是:”的形式而不是每个运算符一个错误。

这样做是为了提供帮助:由于找不到匹配项,它假定您实际上可能指的是 任何 可能的可用运算符。

【讨论】:

  • 有几个人回答,但这是第一个。谢谢。
【解决方案2】:

请注意,我使用vector 而不是string 并不是拼写错误。我故意这样做是为了让编译器无法理解 std::string 是什么,因此它必须四处搜索以确定+ 指的是哪个运算符。

这是不正确的。编译器确实知道std::string 是什么。例如,考虑以下程序编译得很好(使用 Visual C++ 2010,但它也应该在 2005 中工作):

#include <vector>

int main() {
    std::string s;
}

在使用 Visual C++ 时(至少是 Visual C++ 2005 到 2010;我不能说其他版本),包括 &lt;vector&gt; 还包括任何内部标头声明和定义 std::basic_stringstd::string。这是 C++ 语言标准所允许的。

但是,您显然没有得到采用std::string(或更准确地说是std::basic_string)操作数的operator+ 重载。要获得这些,您必须包含 &lt;string&gt;

编译器完全知道std::string 是什么,但是当它尝试为两个std::string 对象查找operator+ 重载时,它会失败并报告它考虑过但拒绝的所有operator+ 重载。

【讨论】:

  • +1,谢谢。关键点似乎在你的最后一段。也就是说,如果它只找到operator+ 的模板版本,那么它会产生所有它无法找到转换的东西,并且它是故意这样做的。但是,似乎如果有operator+ 的非模板重载,那么它只会抱怨一次。我想这毕竟是一个功能。
  • 您的回答为我节省了大量时间。 MS 糟糕记录 (IMO) 的一件事正是包括用于给定的类或一组函数。我正在重用代码并且不知道我需要 ...毕竟我已经有了 。非常感谢!
  • @ChronoFish: &lt;string.h&gt;&lt;string&gt; 是两个不同的头文件:前者来自 C 标准库;后者来自 C++ 标准库。除了名称相似之外,它们完全不相关。哪些标头包括哪些其他标头在很大程度上未指定。由程序员决定是否包含正确的标题。您可以从多个位置之一(例如cppreference.com)中找出需要包含的标头。 MSDN 也应该有这个文档。
【解决方案3】:

它不只是抱怨不存在合适的运营商。

如果发生这种情况,那是 IMO 一个简单愚蠢的编译器行为。当然,它应该首先告诉您出了什么问题(即不存在合适的运算符),然后列出所有考虑的模板。

一个是它为每个涉及模板的重载 + 运算符提供此错误。这告诉我编译器绝对没有办法排除这些 + 中的任何一个都没有意义;

一般来说,它不能这样做,因为例如std::_Vector_iterator&lt;_Ty,_Alloc&gt; 可能是std::basic_string&lt;&gt; 的基类,这意味着一个运算符重载可能是可行的并且推导成功。参数推导是在那个阶段编译 C++ 的阶段,它计算出这些东西,编译器只是将参数推导的结果报告给你。

【讨论】:

  • VC10 确实报告不存在合适的运算符 (error C2676: binary '+' : 'std::string' does not define this operator or a conversion to a type acceptable to the predefined operator)。它被报告在考虑的重载列表的末尾,而不是之前,但至少它被报告了。 (我不知道 OP 的编译器 VC8 是否也这样做了;我没有安装它来检查。)
  • 确实如此 James:1&gt;.\test.cpp(21) : error C2676: binary '+' : 'std::basic_string&lt;_Elem,_Traits,_Ax&gt;' does not define this operator or a conversion to a type acceptable to the predefined operator 我只是没有走那么远,因为有 37 个错误。
猜你喜欢
  • 2015-07-09
  • 2018-04-12
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 2015-05-24
  • 2012-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多