c++ - 如何编写具有高重载优先级的类标准函数

标签 c++ c++11 ambiguous argument-dependent-lookup

在一个通用函数中,我使用以下成语,

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
    ... other stuff here...
    using std::copy;
    copy(first, second, d_first);
}

do_something 是一个通用函数,它不应该知道任何其他库的任何具体信息(可能除了 std::)。 p>

现在假设我的命名空间 N 中有几个迭代器。

namespace N{

  struct itA{using trait = void;};
  struct itB{using trait = void;};
  struct itC{using trait = void;};

}

我想在这个命名空间中为这些迭代器重载拷贝。 我自然会这样做:

namespace N{
    template<class SomeN1, class SomeN2>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
        std::cout << "here" << std::endl;
    }
}

但是,当我使用 N::AN::BN::C 调用 do_something > 参数我得到“模棱两可的复制调用”,即使它们与 N::copy 位于相同的命名空间中。

有没有办法在上述原始函数的上下文中战胜std::copy

我认为如果我对模板参数施加约束,那么 N::copy 将是首选。

namespace N{
    template<class SomeN1, class SomeN2, typename = typename SomeN1::trait>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
        std::cout << "here" << std::endl;
    }
}

但这无济于事。

我可以尝试哪些其他变通方法让通用调用 copy 更喜欢参数命名空间中的拷贝而不是 std::copy

完整代码:

#include<iostream>
#include<algorithm>
namespace N{
  struct A{};
  struct B{};
  struct C{};
}

namespace N{
    template<class SomeN1, class SomeN2>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
        std::cout << "here" << std::endl;
    }
}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
    using std::copy;
    copy(first, second, d_first); // ambiguous call when It is from namespace N (both `std::copy` and `N::copy` could work.
}

int main(){
    N::A a1, a2, a3;
    do_something(a1, a2, a3); 
}

一个典型的错误信息是

错误:重载‘copy(N::A&, N::A&, N::A&)’的调用不明确


我是否认为 C++ 概念会通过更喜欢具有更多约束而不是更少约束的函数调用来提供帮助?

最佳答案

您可以将 copy() 声明为 public friend function在您的迭代器类中。 这有点像替代部分特化(这对函数来说是不可能的),因此重载决议会更喜欢它们,因为它们更特化:

#include <iostream>
#include <algorithm>
#include <vector>

namespace N
{
    template<class SomeN1, class SomeN2>
    SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first)
    {
        std::cout << "here" << std::endl;
        return d_first;
    }

    template <class T>
    struct ItBase
    {
        template <class SomeN2>
        friend SomeN2 copy(T first, T last, SomeN2 d_first)
        {
            return N::copy(first, last, d_first);
        }
    };

    struct A : ItBase<A>{};
    struct B : ItBase<B>{};
    struct C : ItBase<C>{};
}

template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
    using std::copy;
    copy(first, second, d_first);
}

int main(){
    N::A a1, a2, a3;
    std::cout << "do something in N:" << std::endl;
    do_something(a1, a2, a3); 

    std::vector<int> v = {1,2,3};
    std::vector<int> v2(3);
    std::cout << "do something in std:" << std::endl;
    do_something(std::begin(v), std::end(v), std::begin(v2));
    for (int i : v2)
        std::cout << i;
    std::cout << std::endl;
}

this demo验证它是否有效。

我介绍了一个通用基类,它为所有迭代器声明必要的 friend 。因此,无需像您尝试的那样声明标签,您只需从 ItBase 继承即可。

注意:如果 N::copy() 应该仅与 N 中的这些迭代器一起使用,则可能不再需要它,因为这些友元函数将是公开的无论如何都在 N 中可见(就好像它们是自由函数一样)。


更新:

在评论中,有人建议,如果 N 中的迭代器无论如何都有一个公共(public)基类,则只需用这个基类声明 N::copy,例如

namespace N
{
    template <class SomeN2>
    SomeN2 copy(ItBase first, ItBase last, SomeN2 d_first) { ... }
}

不幸的是,这会产生与期望相反的效果:std::copy 总是优于 N::copy 因为如果你传递一个A,它必须被向下转换以匹配 N::copystd::copy 不需要转换。 Here您可以看到显然 std::copy 被尝试调用(这会导致错误,因为 N::A 缺少一些类型定义)。

因此,您不能将通用基类用于 N::copy 的签名。我在解决方案中使用它的唯一原因是避免重复代码(必须在每个迭代器类中声明友元函数)。我的 ItBase 根本不参与重载决议。

但是请注意,如果您的迭代器碰巧有一些您想在 N::copy 的实现中使用的公共(public)成员(无论是否从某个公共(public)基类派生并不重要) ,你可以用我上面的解决方案来做到这一点:

namespace N
{
    template <class T>
    struct ItBase
    {
        template <class SomeN2>
        friend SomeN2 copy(T first, T last, SomeN2 d_first)
        {
            first.some_member();
            last.some_member();
            return d_first;
        }
    };

    struct A : ItBase<A>{ void some_member() {} };
    struct B : ItBase<B>{ void some_member() {} };
    struct C : ItBase<C>{ void some_member() {} };
}

here它是如何工作的。


在同一行中,如果 A、B、C 具有共同行为,则可以用以某种方式参数化的共同模板类替换它们。

namespace N
{
    template <class T, int I>
    struct ItCommon
    {
       ...
    };
    using A = ItCommon<double,2>;
    using B = ItCommon<int, 3>;
    using C = ItCommon<char, 5>;
}
...
namespace N{
    template<class T, int I, class Other>
    SomeN2 copy(ItCommon<T, I> first, ItCommon<T, I> last, Other){
        ...
    }
} 

由于这个(非 friend )copy 函数肯定比 std::copy 更受限制,并且由于 ADL,当其中一个参数属于 N 命名空间。另外,作为非好友,这个 copy 函数是一个可选组件。

关于c++ - 如何编写具有高重载优先级的类标准函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54400763/

相关文章:

c++ - 如何防止在随机生成器中多次生成整数

c++ - 如何在C++中制作无限序列

方法的Java模糊类型?

c++ - 递归算法时间复杂度 : Coin Change

c++ - 在使用 g++ 创建的共享库中隐藏实例化模板

c++ - 预解析(缓存)printf 风格的格式化字符串

c++ - 静态/动态链接的静态初始化顺序问题

c++ - 解决模板不明确的问题

c++ - 运算符 = 不明确 (C++)

多种数据类型的 C++ 堆栈(RPN vector 计算器)