C++ 如何编写具有高过载优先级的标准函数
在泛型函数中,我使用以下习惯用法:C++ 如何编写具有高过载优先级的标准函数,c++,c++11,ambiguous,argument-dependent-lookup,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); } 我想重载此命名空间中这些迭代器的副本。 当然,我会: namespace N{ template&l
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);
}
我想重载此命名空间中这些迭代器的副本。
当然,我会:
namespace N{
template<class SomeN1, class SomeN2>
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);
}
#包括
#包括
名称空间N{
结构A{};
结构B{};
结构C{};
}
名称空间N{
模板
一些副本(一些在前,一些在后,一些在前){
std::coutOK,在@paler123上构建,但不检查现有类型,而是检查It1
是否为指针:
namespace N{
struct A{};
struct B{};
struct C{};
}
namespace N{
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1, SomeN1, SomeN2 c){
std::cout << "here" << std::endl;
return c;
}
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
if constexpr (std::is_pointer_v<It1>) {
std::copy(first, second, d_first);
}
else
{
copy(first, second, d_first);
}
}
int main(){
N::A a1, a2, a3;
do_something(a1, a2, a3);
int* b1, *b2, *b3;
do_something(b1, b2, b3);
}
名称空间N{
结构A{};
结构B{};
结构C{};
}
名称空间N{
模板
SomeN2副本(SomeN1,SomeN1,SomeN2 c){
std::cout一种可能的解决方案是使用另一个函数模板名称和类型鉴别器来允许参数相关名称查找,以在参数的命名空间中查找关联函数:
template<class T> struct Tag {};
template<class T> Tag<void> tag(T const&);
template<class It1, class It2>
void mycopy(It1 first, It1 second, It2 d_first, Tag<void>) {
std::cout << "std::copy\n";
}
template<class It1, class It2>
void mycopy(It1 first, It1 second, It2 d_first) {
mycopy(first, second, d_first, decltype(tag(first)){}); // Discriminate by the type of It1.
}
namespace N{
struct itA{using trait = void;};
Tag<itA> tag(itA);
template<class It1, class It2>
void mycopy(It1 first, It1 second, It2 d_first, Tag<itA>) {
std::cout << "N::mycopy\n";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
模板结构标记{};
模板标记(T常量&);
模板
无效mycopy(It1第一,It1第二,It2 d_第一,标签){
这似乎满足了您的要求:
namespace SpecCopy {
template <typename A, typename B, typename C>
void copy(A &&a, B &&b, C &&c) {
std::copy(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c));
}
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
using namespace SpecCopy;
copy(first, second, d_first);
}
然后,do\u something
将调用N::copy
如果您这样做:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
int *a1, *a2, *a3;
do_something(a1, a2, a3);
然后发生了与以前相同的事情:dou\u something
将调用SpecCopy::copy
,它将调用std::copy
,建议您看看这个功能强大的新库
此函数完全满足您的要求:
#include <boost/hof.hpp>
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first){
namespace hof = boost::hof;
auto my_copy = hof::first_of(
[](auto first, auto second, auto d_first) -> decltype(N::copy(first, second, d_first))
{
return N::copy(first, second, d_first);
},
[](auto first, auto second, auto d_first) -> decltype(std::copy(first, second, d_first))
{
return std::copy(first, second, d_first);
});
my_copy(first, second, d_first);
}
#包括
模板
无效做某事(It1先,It1秒,It2先){
名称空间hof=boost::hof;
自动我的拷贝=hof::第一个(
[](自动第一,自动第二,自动第一)->decltype(N::copy(第一,第二,第一)
{
返回N::复制(第一,第二,d_第一);
},
[](自动第一,自动第二,自动第一)->decltype(std::copy(第一,第二,第一)
{
返回std::复制(第一、第二、d_第一);
});
我的复印件(第一、第二、第一);
}
< > >代码> HOF::<代码> >第一个lambda,其返回类型被推断为合法表达式的结果类型。 <>在C++ 11中,你可以使用标签调度。如果你能对自定义迭代器做一些改动,事情就会简单一点。
#include <iostream>
#include <algorithm>
#include <vector>
#include <type_traits>
// indicates that the type doesn't have a tag type (like pointers and standard iterators)
struct no_tag{};
namespace detail
{
template <typename T>
auto tag_helper(int) -> typename T::tag;
template <typename>
auto tag_helper(long) -> no_tag;
}
// get T::tag or no_tag if T::tag isn't defined.
template <typename T>
using tag_t = decltype(detail::tag_helper<T>(0));
namespace N
{
struct my_iterator_tag {};
struct A{ using tag = my_iterator_tag; };
struct B{ using tag = my_iterator_tag; };
struct C{ using tag = my_iterator_tag; };
}
namespace N
{
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copy\n";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, my_iterator_tag)
{
// your custom copy
std::cout << "custom copy function\n";
return {};
}
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first)
{
return copy_helper(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first), tag_t<SomeN1>{});
}
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
N::copy(first, second, d_first);
}
int main()
{
N::A a1, a2, a3;
std::cout << "using custom iterator: ";
do_something(a1, a2, a3);
std::cout << "using vector iterator: ";
std::vector<int> v;
do_something(std::begin(v), std::end(v), std::begin(v));
std::cout << "using pointer: ";
int* ptr = new int[10];
do_something(ptr, ptr + 5, ptr);
return 0;
}
您可以在迭代器类中将copy()
声明为。
这在某种程度上可以替代部分专门化(这对于函数来说是不可能的),因此重载解析将首选它们,因为它们更专业:
#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;
}
不幸的是,这会产生与所需的相反的效果:std::copy
总是比N::copy
更受欢迎,因为如果您传递A
的实例,它必须向下转换才能匹配N::copy
,而std::copy
不需要强制转换试图调用ystd::copy
(这会给出一个错误,因为N::A
缺少一些typedef)
因此,您不能利用公共基类作为N::copy
的签名。我在解决方案中使用公共基类的唯一原因是避免重复代码(必须在每个迭代器类中声明友元函数)。我的ItBase
根本不参与重载解析
但是,请注意,如果您的迭代器碰巧有一些要在N::copy
的实现中使用的公共成员(是否派生自某个公共基类并不重要),您可以使用上面的我的解决方案来实现,如下所示:
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() {} };
}
名称空间N
{
模板
结构ItBase
{
模板
朋友:一些副本(T第一,T最后,一些d_第一)
{
首先,某个_成员();
最后,一些成员();
首先返回d_;
}
};
结构A:基于它的工作原理
在同一行中,如果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){
...
}
}
名称空间N
{
模板
结构ItCommon
{
...
};
使用A=ItCommon;
使用B=ItCommon;
使用C=ItCommon;
}
...
名称空间N{
模板
SomeN2副本(ItCommon在前,ItCommon在后,其他){
...
}
}
由于此(非友元)copy
函数肯定比std::copy
更受约束,并且由于ADL,当其中一个参数属于N
命名空间时,它将具有高优先级。此外,作为非友元,此copy
函数是可选组件。(这些注释现在已集成到我对@sebrockm答案的编辑中)
为了便于讨论,我将用另一个选项回答我自己的问题
这不是很好,因为它需要在另一个模板类中包装所有的N::
类(这里称为wrap
)好的是,做点什么
或者N
类都不需要知道特殊的N::copy
。代价是main
调用程序必须显式地包装N::
类,这些类很难看,但从耦合的角度来看是好的,因为这是唯一应该知道关于整个系统
#include <iostream>
#include <algorithm>
#include <vector>
namespace N{
struct A{};
struct B{};
struct C{};
}
namespace N{
template<class S> struct wrap : S{};
template<class SomeN1, class SomeN2>
SomeN2 copy(wrap<SomeN1> first, wrap<SomeN1> last, wrap<SomeN2> d_first)
{
std::cout << "here" << std::endl;
return d_first;
}
}
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::wrap<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;
}
#包括
#包括
#包括
名称空间N{
结构A{};
结构B{};
结构C{};
}
名称空间N{
模板结构换行:S{};
模板
SomeN2副本(先包装,后包装,先包装d_)
{
std::cout不是dupe,但是。显然,错误来自总是使用std::copy;
@alfC执行。您的N::copy
也是一个函数模板,它没有与命名空间N
关联的参数。因此,就这一目的而言,它并不比std::copy
更好
#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;
}
namespace N
{
template <class SomeN2>
SomeN2 copy(ItBase first, ItBase last, SomeN2 d_first) { ... }
}
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() {} };
}
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){
...
}
}
#include <iostream>
#include <algorithm>
#include <vector>
namespace N{
struct A{};
struct B{};
struct C{};
}
namespace N{
template<class S> struct wrap : S{};
template<class SomeN1, class SomeN2>
SomeN2 copy(wrap<SomeN1> first, wrap<SomeN1> last, wrap<SomeN2> d_first)
{
std::cout << "here" << std::endl;
return d_first;
}
}
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::wrap<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;
}