【问题标题】:Why is std::endl generating this cryptic error message?为什么 std::endl 会生成这个神秘的错误消息?
【发布时间】:2014-08-14 04:47:37
【问题描述】:

如果我尝试编译以下代码,我会收到以下编译器错误(请参阅代码。)如果删除了 std::endl,它编译不会出错。

#include <iostream>
#include <sstream>
#include <utility>

namespace detail
{
    template <class T>
    void print(std::ostream& stream, const T& item)
    {
        stream << item;
    }

    template <class Head, class... Tail>
    void print(std::ostream& stream, const Head& head, Tail&&... tail)
    {
        detail::print(stream, head);
        detail::print(stream, std::forward<Tail>(tail)...);
    }
}

template <class... Args>
void print(std::ostream& stream, Args&&... args)
//note: candidate function not viable: requires 3 arguments, but 4 were provided
{
    std::stringstream ss;
    detail::print(ss, std::forward<Args>(args)...);
    stream << ss.rdbuf();
}

int main()
{
    print(std::cout, "The answer is ", 42, std::endl);
    //error: no matching function for call to 'print'
}

【问题讨论】:

  • 这并不神秘。这意味着没有 print 的定义接受这些论点。我不确定print 是从哪里来的。为什么不直接使用std::cout &lt;&lt; "The answer is " &lt;&lt; 42 &lt;&lt; std::endl;
  • 您是在问“代码有什么问题?”还是“为什么错误信息不清楚?”
  • @sjeohp 你确定吗?这听起来很疯狂。
  • 这是用于输出的标准 c++ 语法
  • std::endl 是一个模板。

标签: c++ c++11 compiler-errors iostream variadic-templates


【解决方案1】:

std::endl 是一个函数模板。使用时,其模板参数必须由编译器显式指定或推导。

std::ostream 过载:

basic_ostream<charT,traits>& operator<<(
    basic_ostream<charT,traits>& (*pf) (basic_ostream<charT,traits>&) );

当我们使用

std::cout << std::endl;

编译器推导出用于std::endl 的类型。由于您在调用print 时无法依靠自动类型推断,因此您必须明确说明您要使用哪个版本的std::endl

以下应该有效:

print(std::cout, "The answer is ", 42, std::endl<char, std::char_traits<char>>);

更新

我使用以下精简代码来跟踪问题:

#include <iostream>

namespace detail
{
   template <class T>
      void print(std::ostream& stream, const T& item)
      {
         stream << item;
      }
}

int main()
{
    // detail::print(std::cout, std::endl);
    detail::print(std::cout, std::endl<char, std::char_traits<char>>);
}

【讨论】:

  • 小吹牛,是模板类型推演,不是ADL。 (ADL 是指基于参数类型的名称查找;但是这里名称查找成功)
  • 为了详细说明@Matt 的观点,ADL 正在找到here endl。在您的示例中不会发生这种情况,因为没有对endl的不合格引用
  • @MattMcNabb 和@Praetorian,我很难弄清楚在std::cout &lt;&lt; std::endl 中使用时如何为std::endl 使用正确的模板参数。我在标准中查找了14.6.4.2 Candidate functions,但这似乎无法解释。有什么指点吗?
  • Praetorian 可以给出更具体的答案;但从广义上讲:它与重载 basic_ostream&lt;charT,traits&gt;&amp; operator&lt;&lt;(ios_base&amp; (*pf)(ios_base&amp;)); 匹配。这不是模板函数,因此在尝试一次推断出两组模板参数时,它与此处的 OP 问题没有相同的问题。它推导出std::endl 的参数,使其与该函数指针类型匹配。
  • @MattMcNabb std::cout &lt;&lt; std::endl; 有一个 better match。请注意,operator&lt;&lt; 本身是由 ADL 找到的,但不是 std::endl
【解决方案2】:

我认为这是因为如果您传递函数模板,则模板类型推导失败。无法推断出实例化endl的参数。

注意endl的定义是:

template <class charT, class traits> 
basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os);

更简单的例子:

template<class U> void func(U &u) { }

template<class T>
void print(const T &item) { }

int main()
{
print(func);    // error: matching function for call to 'print(<unresolved overloaded function type>)'
}

出现您的错误消息是因为它尝试了各种方法来将您的函数调用与参数包相匹配,但都没有奏效。

【讨论】:

    【解决方案3】:

    您可以通过自己定义一个简单的endl (Live Demo) 来避免该问题:

    constexpr struct endl_ {
        friend std::ostream& operator << (std::ostream& os, const endl_&) {
            os << '\n'; // << std::flush;
            return os;
        }
    } endl;
    
    template <class... Args>
    void print(std::ostream& stream, Args&&... args)
    {
        std::stringstream ss;
        std::initializer_list<int>{0, (void(ss << std::forward<Args>(args)), 0)...};
        stream << ss.rdbuf();
    }
    
    int main()
    {
        print(std::cout, "The answer is ", 42, endl);
        //error: no matching function for call to 'print'
        print(std::cout, "The answer is NOT ", 13, endl);
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-09
      • 2015-07-13
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      相关资源
      最近更新 更多