【问题标题】:Why does a global merge() function conflict with std::merge()?为什么全局 merge() 函数与 std::merge() 冲突?
【发布时间】:2013-10-28 22:43:05
【问题描述】:

考虑以下代码:

#include <vector>
#include <algorithm>

template <typename Input1, typename Input2, typename Output>
void merge(Input1 begin1, Input1 end1, Input2 begin2, Input2 end2, Output out)
{
}

int main()
{
    std::vector<int> a = {1, 2};
    int b[] = {3, 4};
    int c[4];

    merge(a.begin(), a.end(), b, b + 2, c);
}

编译产量:

$ clang++ -std=c++11 -stdlib=libc++ merge.cpp 
merge.cpp:15:5: error: call to 'merge' is ambiguous
    merge(a.begin(), a.end(), b, b + 2, c);
    ^~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:4056:1: note: 
      candidate function [with _InputIterator1 = std::__1::__wrap_iter<int *>,
      _InputIterator2 = int *, _OutputIterator = int *]
merge(_InputIterator1 __first1, _InputIterator1 __last1,
^
merge.cpp:5:6: note: candidate function [with Input1 = std::__1::__wrap_iter<int
      *>, Input2 = int *, Output = int *]
void merge(Input1 begin1, Input1 end1, Input2 begin2, Input2 end2, Output out)
     ^
1 error generated.

编译器版本:

$ clang++ --version
Apple LLVM version 5.0 (clang-500.2.78) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

为什么对merge 的调用不明确?不确定我的意思是::merge() 还是std::merge(),虽然很明显(?)它应该是::merge(),因为我没有指定任何using 指令。我的 merge 函数位于全局命名空间中,我认为它不会与 std 命名空间中的任何内容冲突(因为这是命名空间的要点,对吧?)。如果我像其他数组一样将a 更改为int 数组,它的编译不会有任何歧义。此外,添加冒号并调用 ::merge() 也可以正常工作。

所以我的问题是:这是 Clang 中的错误,还是我对命名空间有误解?当两个函数不在同一个命名空间中并且我没有使用任何 using 指令使 std::merge() 可见时,为什么我对 merge() 的调用会导致歧义?

【问题讨论】:

  • 对于那些想知道我为什么要这样做的人,我只是在练习实现合并排序。

标签: c++ compiler-errors clang


【解决方案1】:

问题在于std::vector&lt;T&gt;::iterator 可能是类类型(在您的情况下,它类类型):在重载解析期间,编译器会找到函数的所有可见声明。为此,它会查找可能与其参数相关的名称空间(这称为参数相关查找)。 std::vector&lt;T&gt;::iterator 类型定义在命名空间 std(或嵌套在其中的命名空间)中,因此,来自命名空间 std 的函数被考虑用于重载决议。由于std::merge() 和您的merge() 都匹配得很好,因此存在歧义。

避免该问题的最简单方法是为函数模板使用不同的名称。隐藏关联的命名空间是可能的,但并不容易:关联的命名空间是从定义类或类模板的位置以及它的基类和模板参数中获取的。因此,为任何迭代器类型创建包装器模板是不够的,因为它仍然将原始命名空间与类型相关联。您可能会尝试使您的函数模板更好地匹配,但鉴于它与标准算法一样通用,这也不太可行。

【讨论】:

    【解决方案2】:

    这是由于对 std 命名空间中的迭代器的参数相关查找 (http://en.cppreference.com/w/cpp/language/adl)。

    可以::merge来获取你的函数,但我宁愿使用不同的名字。

    【讨论】:

      猜你喜欢
      • 2017-12-28
      • 2016-11-18
      • 2020-08-25
      • 2018-07-26
      • 2017-12-31
      • 1970-01-01
      • 2017-11-15
      • 2013-02-19
      • 1970-01-01
      相关资源
      最近更新 更多