【问题标题】:msvc++ doesn't see overloaded operator<<msvc++ 没有看到重载的运算符<<
【发布时间】:2015-05-29 14:00:07
【问题描述】:

我正在将一段为 Linux 编写并使用 gcc v4.8.2(使用 -std=c++11)编译的代码移植到 Windows。其中一部分是 MSVC++ 2013 不同意的重载 operator&lt;&lt;。下面是一个精简的例子。

我们已经定义了一个支持标准流操纵器功能的自定义流类。出于这个原因,我们重载了operator&lt;&lt;,它接受一个函数指针作为第二个参数。

如果我定义了 operator

错误 C2676:二进制“

但是,如果我将第二个参数的类型指定为 std::ostream 而不是模板化的 std::basis_ostream&lt;..,..&gt;,则代码将按预期工作。

起初我以为我搞砸了模板参数。但是,如果我定义一个任意模板函数而不是 operator&lt;&lt; 的重载,则代码编译得很好。这是怎么回事?

示例

#include "stdafx.h"

#include <iostream>
#include <sstream>
#include <string>

struct foobar {
    std::stringstream s_;
};

/* Causes compiler error C2676
template <typename CharT = char, typename Traits = std::char_traits<CharT> >
foobar& operator<<(foobar& foo, std::basic_ostream<CharT, Traits>& (*manip)(std::basic_ostream<CharT, Traits>&))
{
    foo.s_ << manip;
    return foo;
}
 */

/* works as intendend */
foobar& operator<<(foobar& foo, std::ostream& (*manip)(std::ostream&))
{
    foo.s_ << manip;
    return foo;
}

/* works too */
template <typename CharT = char, typename Traits = std::char_traits<CharT> >
foobar& qux(foobar& foo, std::basic_ostream<CharT, Traits>& (*manip)(std::basic_ostream<CharT, Traits>&))
{
    foo.s_ << manip;
    return foo;
}

int _tmain(int argc, _TCHAR* argv[])
{
    foobar foo;
    foo << std::endl;

    qux(foo, std::endl);
    return 0;
}

【问题讨论】:

  • 看起来像一个编译器错误。 g++5.1 和 clang++3.6 都接受程序,以及 vc++19(参见例如webcompiler.cloudapp.net
  • std::endl 不是函数,它是模板函数。你将它传递给一个特定的签名,它会发现过载。你将一个带有类型模板的函数传递给它,它搞砸了?但仅当您进行运算符重载时。
  • 请注意,CharTTraits 永远无法推导出来(因为std::endl 是一个函数模板),因此您也可以使用您的第二个版本的operator&lt;&lt;
  • 真的,&lt;&lt;manip 应该从stringstream 推导出它的CharTTraits 不?
  • @Yakk 怎么样? decltype(foobar::s_)?

标签: c++11 visual-c++


【解决方案1】:

默认参数、重载运算符和模板函数参数重载解析似乎存在某种错误。

这些都很复杂,所以可以理解。

好消息是,您不应该只服用 任何 iomanip - 您应该服用特定的。

你可以硬编码,也可以这样推断:

// Get the nth type from a template instance:
template<class...>struct types{using type=types;};
template<class T, size_t N>
struct nth{};
template<class T, size_t N>
using nth_t=typename nth<T,N>::type;
template<template<class...>class Z, class T0, class...Ts, size_t N>
struct nth<Z<T0,Ts...>,N>:nth<types<Ts...>,N-1>{};
template<template<class...>class Z, class T0, class...Ts>
struct nth<Z<T0,Ts...>,0>{using type=T0;};

// From a String type, produce a compatible basic_stream type:
template<class String>
using compat_stream = std::basic_ostream<nth_t<String,0>, nth_t<String,1>>;

// From a type T, produce a signature of a function pointer that
// pass-through manipulates it:
template<class T>
using manip_f = T&(*)(T&);

// From a String type, produce a compatible io-manipulator:
template<class String>
using io_manip_f = manip_f< compat_stream<String> >;

// The return of foobar:
struct foobar {
  std::stringstream s_;
  // the type of manipulators that is compatible with s_:
  using manip_f = io_manip_f<std::stringstream> manip;
  // a Koenig operator<<:
  friend foobar& operator<<( foobar& self, manip_f manip ) {
    self.s_ << manip;
    return self;
  }
};

【讨论】:

  • 使用stringstream 作为“字符串类型”有点混乱。
  • @dyp 即使ostream 也符合上述定义的“字符串类型”。是的,很奇怪:这是我想从中提取“字符串类型信息”的类型,即字符类型和特征。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
  • 1970-01-01
  • 2016-01-24
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
相关资源
最近更新 更多