【问题标题】:Type mismatch in overloaded operator (writing pipelines)重载运算符中的类型不匹配(编写管道)
【发布时间】:2015-01-21 20:19:26
【问题描述】:

只是测试/学习在 C++ 中编写管道,重载 | 运算符,以下程序 编译失败:

二进制表达式候选模板的无效操作数被忽略: ... 无法将 'bitset' 与 'vector' 匹配

似乎编译器正在尝试使用标准 |定义。 该代码适用于显式调用和显式类型参数集。

// g++ -std=c++11 Pipeline.cpp 
#include <iostream>
#include <vector>

// ..............................................................
// ..............................................................
std::string dup (std::string s) {
  return s + s;
}

// ..............................................................
// ..............................................................
template <typename TI, typename TO>
std::vector<TO> operator | (const std::vector<TI> & in, std::function<TO(TI)> f) {
  std::vector<TO> out;
  for (auto i : in) {
    out.push_back ( f(i) );
  }
  return out;
}

// ..............................................................
// ..............................................................
int main () {
  std::cout << " hello " << std::endl;

  std::vector<std::string> vs = {"one", "two", "three", "four", "five"};

  auto res = vs | dup;
  // OK: vector<string> res = operator|<string,string> (vs, dup);

  for (auto s : res) { std::cout << s << std::endl; }

} // ()

完整的错误信息:

Pipeline.cpp:29:17: error: invalid operands to binary expression
      ('std::vector<std::string>' and 'std::string (*)(std::string)')
  auto res = vs | dup;
             ~~ ^ ~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/bitset:1045:1: note: 
      candidate template ignored: could not match 'bitset' against 'vector'
operator|(const bitset<_Size>& __x, const bitset<_Size>& __y) _NOEXCEPT
^
Pipeline.cpp:13:17: note: candidate template ignored: could not match
      'function<type-parameter-0-1 (type-parameter-0-0)>' against
      'std::__1::basic_string<char> (*)(std::__1::basic_string<char>)'
std::vector<TO> operator | (const std::vector<TI> & in, std::function<TO...
                ^
1 error generated.

【问题讨论】:

  • 这是完整的错误输出吗?为什么它抱怨bitset,因为显示的源代码中没有位集?
  • @JoachimPileborg 我刚刚附上了完整的错误信息。
  • 问题出在模板函数中。如果你用std::string 替换template就可以了。
  • 函数指针不是std::function,所以模板参数推导会失败。

标签: c++ c++11


【解决方案1】:

模板参数推导不查看隐式转换。函数指针不是std::function,因此编译器无法推断出使用哪些模板参数来实例化您的operator|。一般来说,std::function&lt;...&gt;(其中... 包含模板参数)不是接受任意函数或函数对象的正确方式。

相反,接受函子的任何类型:

template <typename TI, typename F>
/* something */ operator | (const std::vector<TI> & in, F f) 

然后找出返回类型。

返回类型是std::vector,其值类型是通过在in 的元素上调用f 返回的类型——换句话说,decltype(f(in[0]))。但是,f 可以返回引用类型,并且您确实希望在这些情况下向量的值类型是引用的类型。使用std::decay(或std::remove_reference)去除任何引用:

template <typename TI, typename F>
auto operator | (const std::vector<TI> & in, F f) -> std::vector<typename std::decay<decltype(f(in[0]))>::type> {
    std::vector<typename std::decay<decltype(f(in[0]))>::type> out;
    /*...*/
}

Demo.

【讨论】:

  • 我知道这是题外话,但不是真的,无论您可以使用尾随返回类型做什么,您在技术上也可以使用常规返回类型吗?尾随返回类型只是语法糖?
  • @templateboy 不正确,检查C++11 faq
  • @T.C.伟大的 !谢谢。
  • @templateboy 通常,通过调用std::declval&lt;T&gt;() 替换出现的参数t,可以将尾随返回类型转换为非尾随返回类型,其中T 相当于decltype((t))。在这种情况下,返回类型将是 std::vector&lt;typename std::decay&lt;decltype(std::declval&lt;F&amp;&gt;()(std::declval&lt;const std::vector&lt;TI&gt;&amp;&gt;()[0]))&gt;::type&gt;。这样做肯定不利于可读性,并且通过重复引用参数的类型违反了 DRY。
【解决方案2】:

与 T.C. 类似的解决方案

template <typename CONT, typename F>  // types: container , function
auto operator | (const CONT & a, F f) {
  // find out input type and output type
  using TI = typename CONT::value_type;
  TI aux;
  using TO = decltype(f(aux));
  // do the task
  vector<TO> res;
  for (auto & i : a) {
    res.push_back( f(i) );
  }
  return res;
}

编辑 完成“实验”代码。将运算符更改为 是 == :变换,!= 过滤器和 &gt;&gt;= 减少/访问,因此 避免在 vs == dup != lengthy != contains == length &gt;&gt;= add; 等表达式中使用括号(我们需要 transform 和 filer 具有相同的优先级,而 reduce 具有较小的优先级)。不知道这是否会给使用它们的其他表达带来附带问题。

// g++ -std=c++1y Pipeline.cpp
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// ..............................................................
// transform
// ..............................................................
template <typename CONT, typename F>
auto operator == (const CONT & a, F  f) {
  using IT = typename CONT::value_type;
  IT aux;
  using OT = decltype(f(aux));
  vector<OT> res;
  for (auto & i : a) {
    res.push_back( f(i) );
  }
  return res;
}

// ..............................................................
// filter
// ..............................................................
template <typename CONT, typename F>
auto operator != (const CONT & a, F  f) {
  using IT = typename CONT::value_type;
  IT aux;
  vector<IT> res;
  for (auto & i : a) {
    if (f(i)) res.push_back( i );
  }
  return res;
}

// ..............................................................
// reduce / visit
// ..............................................................
template <typename CONT, typename F>
auto operator >>= (const CONT & a, F  f) {
  for (auto & i : a) {
    f(i);
  }
  return f;
}

// ..............................................................
// ..............................................................
int main () {
  vector<string> vs = {"one", "two", "three", "four", "five"};

  int sum = 0;

  auto dup = [] (const string &s) -> string { return s+s; };
  auto lengthy = [] (const string &s) -> bool { return s.length()>6; };
  auto contains = [] (const string &s) -> bool { return s.find('f') != string::npos; };

  auto length = [] (const string &s)   { return s.length(); };
  auto add = [&sum] (int i) { sum += i;};

  // == transform
  // != filter
  // >>= reduce / visit
  vs == dup != lengthy  != contains == length >>= add;

  cout << sum << endl;

  auto res1 =  vs == dup != lengthy  != contains;

  for (auto & s : res1) { cout << s << endl; }

} // ()

【讨论】:

  • 1) 推导的返回类型仅为 C++14; 2)您按值传递容器,这是一个坏主意;和 3) 这个operator | 也将匹配所有内容,这可能也不是一个好主意。您可能想对其进行一些限制。
  • @T.C.感谢您的建议。 1.不会有问题。 2. 改为const CONT &amp; 和 3. 是的,但是| 模仿unix 管道(也许&gt;&gt; 可以)。
  • 问题是这会干扰其他事情。例如,如果你有enum B { x = 0, y = 1, z = 2, w = 4};x | y 会突然停止编译,因为你的重载会被拾取。
  • @T.C.您如何建议将输入类型限制为任何可迭代容器?或者,如果您认为这是一个坏主意,您会建议什么样的约束?
  • @T.C.是的,那将是一个问题。我正在发布我的(目前)实验。也许我会帮助讨论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-19
  • 1970-01-01
  • 1970-01-01
  • 2012-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多