How to write a standard-like function that has high overload priority
In a generic function I use the following idiom,
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
is a generic function that shouldn't know anything specific about any other libraries (except perhaps std::
).
Now suppose I have several iterator in my namespace N
.
namespace N{
struct itA{using trait = void;};
struct itB{using trait = void;};
struct itC{using trait = void;};
}
An I want to overload copy for these iterators in this namespace.
Naturally I would do:
namespace N{
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
std::cout << "here" << std::endl;
}
}
However when I call do_something
with N::A
, N::B
or N::C
argument I get "ambiguous call to copy" even though these are in the same namespace as N::copy
.
Is there a way to win over std::copy
in the context of the original function above?
I though that if I put constrains over the template arguments then N::copy
would be preferred.
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;
}
}
but it doesn't help.
What other workarounds can I try for the generic call to copy to prefer to a copy in the namespace of arguments rather than std::copy
.
Complete code:
#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);
}
A typical error message is
error: call of overloaded ‘copy(N::A&, N::A&, N::A&)’ is ambiguous
Am I right to think that C++ Concepts will help here by preferring function calls with more contraints than less constraints?
c++ c++11 ambiguous argument-dependent-lookup
|
show 11 more comments
In a generic function I use the following idiom,
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
is a generic function that shouldn't know anything specific about any other libraries (except perhaps std::
).
Now suppose I have several iterator in my namespace N
.
namespace N{
struct itA{using trait = void;};
struct itB{using trait = void;};
struct itC{using trait = void;};
}
An I want to overload copy for these iterators in this namespace.
Naturally I would do:
namespace N{
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
std::cout << "here" << std::endl;
}
}
However when I call do_something
with N::A
, N::B
or N::C
argument I get "ambiguous call to copy" even though these are in the same namespace as N::copy
.
Is there a way to win over std::copy
in the context of the original function above?
I though that if I put constrains over the template arguments then N::copy
would be preferred.
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;
}
}
but it doesn't help.
What other workarounds can I try for the generic call to copy to prefer to a copy in the namespace of arguments rather than std::copy
.
Complete code:
#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);
}
A typical error message is
error: call of overloaded ‘copy(N::A&, N::A&, N::A&)’ is ambiguous
Am I right to think that C++ Concepts will help here by preferring function calls with more contraints than less constraints?
c++ c++11 ambiguous argument-dependent-lookup
1
Not a dupe, but related.
– lubgr
yesterday
1
Obviously the error comes from always doingusing std::copy;
.
– Matthieu Brucher
yesterday
3
@alfC YourN::copy
is also a function template that has no arguments associated with namespaceN
. Hence, it is not better thanstd::copy
for the purpose of overload resolution.
– Maxim Egorushkin
yesterday
2
@MatthieuBrucher, if I usestd::copy
then there is no chance the special version ofcopy
is called. The only way would be to overloadstd::copy
(in the namespacestd
) which I don't know if it is allowed.
– alfC
yesterday
1
@geza, the problem is thatdo_something
is a template function that doesn't know about the namespaceN
or the libraryN
.
– alfC
yesterday
|
show 11 more comments
In a generic function I use the following idiom,
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
is a generic function that shouldn't know anything specific about any other libraries (except perhaps std::
).
Now suppose I have several iterator in my namespace N
.
namespace N{
struct itA{using trait = void;};
struct itB{using trait = void;};
struct itC{using trait = void;};
}
An I want to overload copy for these iterators in this namespace.
Naturally I would do:
namespace N{
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
std::cout << "here" << std::endl;
}
}
However when I call do_something
with N::A
, N::B
or N::C
argument I get "ambiguous call to copy" even though these are in the same namespace as N::copy
.
Is there a way to win over std::copy
in the context of the original function above?
I though that if I put constrains over the template arguments then N::copy
would be preferred.
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;
}
}
but it doesn't help.
What other workarounds can I try for the generic call to copy to prefer to a copy in the namespace of arguments rather than std::copy
.
Complete code:
#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);
}
A typical error message is
error: call of overloaded ‘copy(N::A&, N::A&, N::A&)’ is ambiguous
Am I right to think that C++ Concepts will help here by preferring function calls with more contraints than less constraints?
c++ c++11 ambiguous argument-dependent-lookup
In a generic function I use the following idiom,
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
is a generic function that shouldn't know anything specific about any other libraries (except perhaps std::
).
Now suppose I have several iterator in my namespace N
.
namespace N{
struct itA{using trait = void;};
struct itB{using trait = void;};
struct itC{using trait = void;};
}
An I want to overload copy for these iterators in this namespace.
Naturally I would do:
namespace N{
template<class SomeN1, class SomeN2>
SomeN2 copy(SomeN1 first, SomeN1 last, SomeN2 d_first){
std::cout << "here" << std::endl;
}
}
However when I call do_something
with N::A
, N::B
or N::C
argument I get "ambiguous call to copy" even though these are in the same namespace as N::copy
.
Is there a way to win over std::copy
in the context of the original function above?
I though that if I put constrains over the template arguments then N::copy
would be preferred.
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;
}
}
but it doesn't help.
What other workarounds can I try for the generic call to copy to prefer to a copy in the namespace of arguments rather than std::copy
.
Complete code:
#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);
}
A typical error message is
error: call of overloaded ‘copy(N::A&, N::A&, N::A&)’ is ambiguous
Am I right to think that C++ Concepts will help here by preferring function calls with more contraints than less constraints?
c++ c++11 ambiguous argument-dependent-lookup
c++ c++11 ambiguous argument-dependent-lookup
edited yesterday
alfC
asked yesterday
alfCalfC
5,08322959
5,08322959
1
Not a dupe, but related.
– lubgr
yesterday
1
Obviously the error comes from always doingusing std::copy;
.
– Matthieu Brucher
yesterday
3
@alfC YourN::copy
is also a function template that has no arguments associated with namespaceN
. Hence, it is not better thanstd::copy
for the purpose of overload resolution.
– Maxim Egorushkin
yesterday
2
@MatthieuBrucher, if I usestd::copy
then there is no chance the special version ofcopy
is called. The only way would be to overloadstd::copy
(in the namespacestd
) which I don't know if it is allowed.
– alfC
yesterday
1
@geza, the problem is thatdo_something
is a template function that doesn't know about the namespaceN
or the libraryN
.
– alfC
yesterday
|
show 11 more comments
1
Not a dupe, but related.
– lubgr
yesterday
1
Obviously the error comes from always doingusing std::copy;
.
– Matthieu Brucher
yesterday
3
@alfC YourN::copy
is also a function template that has no arguments associated with namespaceN
. Hence, it is not better thanstd::copy
for the purpose of overload resolution.
– Maxim Egorushkin
yesterday
2
@MatthieuBrucher, if I usestd::copy
then there is no chance the special version ofcopy
is called. The only way would be to overloadstd::copy
(in the namespacestd
) which I don't know if it is allowed.
– alfC
yesterday
1
@geza, the problem is thatdo_something
is a template function that doesn't know about the namespaceN
or the libraryN
.
– alfC
yesterday
1
1
Not a dupe, but related.
– lubgr
yesterday
Not a dupe, but related.
– lubgr
yesterday
1
1
Obviously the error comes from always doing
using std::copy;
.– Matthieu Brucher
yesterday
Obviously the error comes from always doing
using std::copy;
.– Matthieu Brucher
yesterday
3
3
@alfC Your
N::copy
is also a function template that has no arguments associated with namespace N
. Hence, it is not better than std::copy
for the purpose of overload resolution.– Maxim Egorushkin
yesterday
@alfC Your
N::copy
is also a function template that has no arguments associated with namespace N
. Hence, it is not better than std::copy
for the purpose of overload resolution.– Maxim Egorushkin
yesterday
2
2
@MatthieuBrucher, if I use
std::copy
then there is no chance the special version of copy
is called. The only way would be to overload std::copy
(in the namespace std
) which I don't know if it is allowed.– alfC
yesterday
@MatthieuBrucher, if I use
std::copy
then there is no chance the special version of copy
is called. The only way would be to overload std::copy
(in the namespace std
) which I don't know if it is allowed.– alfC
yesterday
1
1
@geza, the problem is that
do_something
is a template function that doesn't know about the namespace N
or the library N
.– alfC
yesterday
@geza, the problem is that
do_something
is a template function that doesn't know about the namespace N
or the library N
.– alfC
yesterday
|
show 11 more comments
7 Answers
7
active
oldest
votes
One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:
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::copyn";
}
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::mycopyn";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
add a comment |
You can declare copy()
as a public friend function in your iterator classes.
This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:
#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;
}
See this demo to verify that it works.
I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase
.
Note: If N::copy()
is supposed to work with only these iterators in N
, it might not be needed anymore as these friend functions will be publicly visible in N
anyway (as if they were free functions).
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that theN::iterators
will have to know of all possible algorithms that can implementedcopy
,fill
,accumulate
, etc.
– alfC
14 hours ago
@alfC So you are saying thatcopy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them intoItBase
directly. Or am I misunderstanding you?
– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
|
show 4 more comments
This seems to fulfill your requirements:
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);
}
Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy
, which is a wrapper to std::copy
.
So, if you do:
N::A a1, a2, a3;
do_something(a1, a2, a3);
Then do_something
will call N::copy
.
If you do:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
Then do_something
will call SpecCopy::copy
, which will call std::copy
.
If you do:
int *a1, *a2, *a3;
do_something(a1, a2, a3);
Then same thing happens as before: do_something
will call SpecCopy::copy
, which will call std::copy
.
1
I don't think this solves the issue ... it ties the generaldo_something
to the special code for the iterators ...
– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,do_something
will need to know about theSpecCopy
library/namespace. and if you includeSpecCopy
withdo_something
then there is no customization,N::copy
is never called in this way.
– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is innamespace N
, and you have acopy
function innamespace N
, then with this solution,N::copy
will be called.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
add a comment |
In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.
#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::copyn";
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 functionn";
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;
}
First we change our custom iterators to have a tag
type (maybe change the name to avoid confusion with iterator_category
). tag
can be any type you want, it just has to match the type you use as tag in copy_helper
.
Next, we define a type that allows us to access this tag
type, or to fall back to a default type if tag
doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag
. The tag_t
provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0)
which has two declarations. The first one returns T::tag
while the second one returns no_tag
. Calling tag_helper(0)
will always try to use the first version because int
is a better match for 0
than long
. This means we will always try to access T::tag
first. However if this isn't possible (T::tag
is not defined) SFINAE kicks in and skipps tag_helper(int)
selecting tag_helper(long)
.
Finally, we just have to implement a copy function for each tag (I called it copy_helper
) and another copy function as a wrap around for convinience (I used N::copy
). The wrapper function then creates the proper tag type and calls the correct helper function.
Here is a live example.
Edit
If you move the code around a bit you can disconnect namespace N
and rely on ADL:
#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; };
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 functionn";
return {};
}
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copyn";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}
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;
}
Thank you, butdo_something
will need to know about theN
namespace, which might be in a disconnected library.
– alfC
yesterday
@alfC Well you could also move the code fromcopy
directly todo_something
and include thecopy_helper
with theno_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement thecopy_helper
with its according tag.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think theforward<It1>
is useless unless you pass arguments asIt1&& first
.
– alfC
yesterday
add a comment |
OK, building on @paler123, but without checking for an existing type, but checking if It1
is a pointer instead:
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);
}
Still C++17, but in the case of pointers, we go through the explicit std::copy
otherwise, we rely on ADL.
In general, your issue is a design problem. You want to use std::copy
for all cases, except for objects from N
, and in that case, you hope that ADL will work. But as you forced std::copy
, you remove the option for proper ADL. You can't have everything and you have to redesign your code.
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implementcopy
for them, even ifstd::copy
would be fine for them.
– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
add a comment |
For discussion I will write an answer to my own question with an alternative option.
It is not very nice because it needs to wrap all the N::
classes in another template class (called wrap
here). The good thing is that do_something
nor the N
classes need to know about the special N::copy
. The price is that the main
caller has to explicitly wrap the N::
classes which is ugly but which is fine from the point of view of coupling because this is the only code that should know about the whole system.
#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;
}
add a comment |
Suggest you have a look at the very powerful new Boost.HOF library.
This function does exactly what you want:
#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);
}
hof::first_of
will select the first lambda whose return type is deduced to be the result type of a legal expression.
1
This looks like an interesting library. However the problem is thatdo_something
still needs to know about the library/namespaceN
.
– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for callingcopy
.
– Richard Hodges
yesterday
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54400763%2fhow-to-write-a-standard-like-function-that-has-high-overload-priority%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:
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::copyn";
}
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::mycopyn";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
add a comment |
One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:
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::copyn";
}
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::mycopyn";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
add a comment |
One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:
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::copyn";
}
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::mycopyn";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
One possible solution is to use another function template name and type discriminators to allow argument-dependent name lookup to find the associated function in the namespace of the arguments:
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::copyn";
}
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::mycopyn";
}
}
int main() {
char* p = 0;
mycopy(p, p, p); // calls std::copy
N::itA q;
mycopy(q, q, q); // calls N::mycopy
}
edited yesterday
answered yesterday
Maxim EgorushkinMaxim Egorushkin
86.9k11101184
86.9k11101184
add a comment |
add a comment |
You can declare copy()
as a public friend function in your iterator classes.
This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:
#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;
}
See this demo to verify that it works.
I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase
.
Note: If N::copy()
is supposed to work with only these iterators in N
, it might not be needed anymore as these friend functions will be publicly visible in N
anyway (as if they were free functions).
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that theN::iterators
will have to know of all possible algorithms that can implementedcopy
,fill
,accumulate
, etc.
– alfC
14 hours ago
@alfC So you are saying thatcopy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them intoItBase
directly. Or am I misunderstanding you?
– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
|
show 4 more comments
You can declare copy()
as a public friend function in your iterator classes.
This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:
#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;
}
See this demo to verify that it works.
I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase
.
Note: If N::copy()
is supposed to work with only these iterators in N
, it might not be needed anymore as these friend functions will be publicly visible in N
anyway (as if they were free functions).
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that theN::iterators
will have to know of all possible algorithms that can implementedcopy
,fill
,accumulate
, etc.
– alfC
14 hours ago
@alfC So you are saying thatcopy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them intoItBase
directly. Or am I misunderstanding you?
– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
|
show 4 more comments
You can declare copy()
as a public friend function in your iterator classes.
This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:
#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;
}
See this demo to verify that it works.
I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase
.
Note: If N::copy()
is supposed to work with only these iterators in N
, it might not be needed anymore as these friend functions will be publicly visible in N
anyway (as if they were free functions).
You can declare copy()
as a public friend function in your iterator classes.
This works kind of as a replacement for partial specialization (which is impossible for functions), so that they will be preferred by overload resolution as they are more specialized:
#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;
}
See this demo to verify that it works.
I introduced a common base class that declares the necessary friends for all of your iterators. So, instead of declaring a tag, as you tried, you just have to inherit from ItBase
.
Note: If N::copy()
is supposed to work with only these iterators in N
, it might not be needed anymore as these friend functions will be publicly visible in N
anyway (as if they were free functions).
answered 17 hours ago
sebrockmsebrockm
1,220218
1,220218
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that theN::iterators
will have to know of all possible algorithms that can implementedcopy
,fill
,accumulate
, etc.
– alfC
14 hours ago
@alfC So you are saying thatcopy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them intoItBase
directly. Or am I misunderstanding you?
– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
|
show 4 more comments
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that theN::iterators
will have to know of all possible algorithms that can implementedcopy
,fill
,accumulate
, etc.
– alfC
14 hours ago
@alfC So you are saying thatcopy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them intoItBase
directly. Or am I misunderstanding you?
– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that the
N::iterators
will have to know of all possible algorithms that can implemented copy
, fill
, accumulate
, etc.– alfC
14 hours ago
This is an interesting solution and the friend in the base class allows have the coupling between the iterators and the algorithm declared only once. The problem with this is that the
N::iterators
will have to know of all possible algorithms that can implemented copy
, fill
, accumulate
, etc.– alfC
14 hours ago
@alfC So you are saying that
copy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them into ItBase
directly. Or am I misunderstanding you?– sebrockm
14 hours ago
@alfC So you are saying that
copy
was just an example and actually you want to do this with several std algorithms? In that case you have to implement them anyway, so just put them into ItBase
directly. Or am I misunderstanding you?– sebrockm
14 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
Yes, the point is that these iterators will need to know about any special function in advance. Besides that point, this a good alternative design.
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
BTW, I think public or private doesn't make a difference here for a friend function. friend functions seem to be always accesible (public).
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
your answer inspired me for this yet-another-alternative, stackoverflow.com/a/54425412/225186
– alfC
13 hours ago
|
show 4 more comments
This seems to fulfill your requirements:
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);
}
Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy
, which is a wrapper to std::copy
.
So, if you do:
N::A a1, a2, a3;
do_something(a1, a2, a3);
Then do_something
will call N::copy
.
If you do:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
Then do_something
will call SpecCopy::copy
, which will call std::copy
.
If you do:
int *a1, *a2, *a3;
do_something(a1, a2, a3);
Then same thing happens as before: do_something
will call SpecCopy::copy
, which will call std::copy
.
1
I don't think this solves the issue ... it ties the generaldo_something
to the special code for the iterators ...
– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,do_something
will need to know about theSpecCopy
library/namespace. and if you includeSpecCopy
withdo_something
then there is no customization,N::copy
is never called in this way.
– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is innamespace N
, and you have acopy
function innamespace N
, then with this solution,N::copy
will be called.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
add a comment |
This seems to fulfill your requirements:
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);
}
Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy
, which is a wrapper to std::copy
.
So, if you do:
N::A a1, a2, a3;
do_something(a1, a2, a3);
Then do_something
will call N::copy
.
If you do:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
Then do_something
will call SpecCopy::copy
, which will call std::copy
.
If you do:
int *a1, *a2, *a3;
do_something(a1, a2, a3);
Then same thing happens as before: do_something
will call SpecCopy::copy
, which will call std::copy
.
1
I don't think this solves the issue ... it ties the generaldo_something
to the special code for the iterators ...
– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,do_something
will need to know about theSpecCopy
library/namespace. and if you includeSpecCopy
withdo_something
then there is no customization,N::copy
is never called in this way.
– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is innamespace N
, and you have acopy
function innamespace N
, then with this solution,N::copy
will be called.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
add a comment |
This seems to fulfill your requirements:
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);
}
Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy
, which is a wrapper to std::copy
.
So, if you do:
N::A a1, a2, a3;
do_something(a1, a2, a3);
Then do_something
will call N::copy
.
If you do:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
Then do_something
will call SpecCopy::copy
, which will call std::copy
.
If you do:
int *a1, *a2, *a3;
do_something(a1, a2, a3);
Then same thing happens as before: do_something
will call SpecCopy::copy
, which will call std::copy
.
This seems to fulfill your requirements:
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);
}
Basically, it depends on ADL. If no function found by ADL, then it will use SpecCopy::copy
, which is a wrapper to std::copy
.
So, if you do:
N::A a1, a2, a3;
do_something(a1, a2, a3);
Then do_something
will call N::copy
.
If you do:
std::vector<int> a1, a2;
do_something(a1.begin(), a1.end(), a2.begin());
Then do_something
will call SpecCopy::copy
, which will call std::copy
.
If you do:
int *a1, *a2, *a3;
do_something(a1, a2, a3);
Then same thing happens as before: do_something
will call SpecCopy::copy
, which will call std::copy
.
edited yesterday
answered yesterday
gezageza
12.9k32776
12.9k32776
1
I don't think this solves the issue ... it ties the generaldo_something
to the special code for the iterators ...
– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,do_something
will need to know about theSpecCopy
library/namespace. and if you includeSpecCopy
withdo_something
then there is no customization,N::copy
is never called in this way.
– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is innamespace N
, and you have acopy
function innamespace N
, then with this solution,N::copy
will be called.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
add a comment |
1
I don't think this solves the issue ... it ties the generaldo_something
to the special code for the iterators ...
– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,do_something
will need to know about theSpecCopy
library/namespace. and if you includeSpecCopy
withdo_something
then there is no customization,N::copy
is never called in this way.
– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is innamespace N
, and you have acopy
function innamespace N
, then with this solution,N::copy
will be called.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
1
1
I don't think this solves the issue ... it ties the general
do_something
to the special code for the iterators ...– Daniel Jour
yesterday
I don't think this solves the issue ... it ties the general
do_something
to the special code for the iterators ...– Daniel Jour
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour: Can you give an example or more explanation? I don't understand what you mean.
– geza
yesterday
@DanielJour , geza, as DanielJour says,
do_something
will need to know about the SpecCopy
library/namespace. and if you include SpecCopy
with do_something
then there is no customization, N::copy
is never called in this way.– alfC
yesterday
@DanielJour , geza, as DanielJour says,
do_something
will need to know about the SpecCopy
library/namespace. and if you include SpecCopy
with do_something
then there is no customization, N::copy
is never called in this way.– alfC
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is in
namespace N
, and you have a copy
function in namespace N
, then with this solution, N::copy
will be called.– geza
yesterday
@alfC: Hmm, I thought that you want an ADL based solution. If iterator is in
namespace N
, and you have a copy
function in namespace N
, then with this solution, N::copy
will be called.– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
@alfC: I've edited my answer so you can see what I mean.
– geza
yesterday
add a comment |
In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.
#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::copyn";
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 functionn";
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;
}
First we change our custom iterators to have a tag
type (maybe change the name to avoid confusion with iterator_category
). tag
can be any type you want, it just has to match the type you use as tag in copy_helper
.
Next, we define a type that allows us to access this tag
type, or to fall back to a default type if tag
doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag
. The tag_t
provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0)
which has two declarations. The first one returns T::tag
while the second one returns no_tag
. Calling tag_helper(0)
will always try to use the first version because int
is a better match for 0
than long
. This means we will always try to access T::tag
first. However if this isn't possible (T::tag
is not defined) SFINAE kicks in and skipps tag_helper(int)
selecting tag_helper(long)
.
Finally, we just have to implement a copy function for each tag (I called it copy_helper
) and another copy function as a wrap around for convinience (I used N::copy
). The wrapper function then creates the proper tag type and calls the correct helper function.
Here is a live example.
Edit
If you move the code around a bit you can disconnect namespace N
and rely on ADL:
#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; };
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 functionn";
return {};
}
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copyn";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}
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;
}
Thank you, butdo_something
will need to know about theN
namespace, which might be in a disconnected library.
– alfC
yesterday
@alfC Well you could also move the code fromcopy
directly todo_something
and include thecopy_helper
with theno_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement thecopy_helper
with its according tag.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think theforward<It1>
is useless unless you pass arguments asIt1&& first
.
– alfC
yesterday
add a comment |
In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.
#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::copyn";
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 functionn";
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;
}
First we change our custom iterators to have a tag
type (maybe change the name to avoid confusion with iterator_category
). tag
can be any type you want, it just has to match the type you use as tag in copy_helper
.
Next, we define a type that allows us to access this tag
type, or to fall back to a default type if tag
doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag
. The tag_t
provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0)
which has two declarations. The first one returns T::tag
while the second one returns no_tag
. Calling tag_helper(0)
will always try to use the first version because int
is a better match for 0
than long
. This means we will always try to access T::tag
first. However if this isn't possible (T::tag
is not defined) SFINAE kicks in and skipps tag_helper(int)
selecting tag_helper(long)
.
Finally, we just have to implement a copy function for each tag (I called it copy_helper
) and another copy function as a wrap around for convinience (I used N::copy
). The wrapper function then creates the proper tag type and calls the correct helper function.
Here is a live example.
Edit
If you move the code around a bit you can disconnect namespace N
and rely on ADL:
#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; };
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 functionn";
return {};
}
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copyn";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}
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;
}
Thank you, butdo_something
will need to know about theN
namespace, which might be in a disconnected library.
– alfC
yesterday
@alfC Well you could also move the code fromcopy
directly todo_something
and include thecopy_helper
with theno_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement thecopy_helper
with its according tag.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think theforward<It1>
is useless unless you pass arguments asIt1&& first
.
– alfC
yesterday
add a comment |
In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.
#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::copyn";
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 functionn";
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;
}
First we change our custom iterators to have a tag
type (maybe change the name to avoid confusion with iterator_category
). tag
can be any type you want, it just has to match the type you use as tag in copy_helper
.
Next, we define a type that allows us to access this tag
type, or to fall back to a default type if tag
doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag
. The tag_t
provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0)
which has two declarations. The first one returns T::tag
while the second one returns no_tag
. Calling tag_helper(0)
will always try to use the first version because int
is a better match for 0
than long
. This means we will always try to access T::tag
first. However if this isn't possible (T::tag
is not defined) SFINAE kicks in and skipps tag_helper(int)
selecting tag_helper(long)
.
Finally, we just have to implement a copy function for each tag (I called it copy_helper
) and another copy function as a wrap around for convinience (I used N::copy
). The wrapper function then creates the proper tag type and calls the correct helper function.
Here is a live example.
Edit
If you move the code around a bit you can disconnect namespace N
and rely on ADL:
#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; };
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 functionn";
return {};
}
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copyn";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}
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;
}
In c++ 11 you could use tag dispatch. If you can make a little change to your custom iterators things will be a bit simpler to implement.
#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::copyn";
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 functionn";
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;
}
First we change our custom iterators to have a tag
type (maybe change the name to avoid confusion with iterator_category
). tag
can be any type you want, it just has to match the type you use as tag in copy_helper
.
Next, we define a type that allows us to access this tag
type, or to fall back to a default type if tag
doesn't exist. This will help us distinguish between our custom iterators and standard iterators and pointers. The default type I use is no_tag
. The tag_t
provides us with this functionality by using SFINAE and overload resolution. We call the function tag_helper(0)
which has two declarations. The first one returns T::tag
while the second one returns no_tag
. Calling tag_helper(0)
will always try to use the first version because int
is a better match for 0
than long
. This means we will always try to access T::tag
first. However if this isn't possible (T::tag
is not defined) SFINAE kicks in and skipps tag_helper(int)
selecting tag_helper(long)
.
Finally, we just have to implement a copy function for each tag (I called it copy_helper
) and another copy function as a wrap around for convinience (I used N::copy
). The wrapper function then creates the proper tag type and calls the correct helper function.
Here is a live example.
Edit
If you move the code around a bit you can disconnect namespace N
and rely on ADL:
#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; };
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 functionn";
return {};
}
}
template<class SomeN1, class SomeN2>
SomeN2 copy_helper(SomeN1 first, SomeN1 last, SomeN2 d_first, no_tag)
{
std::cout << "calling std::copyn";
return std::copy(std::forward<SomeN1>(first), std::forward<SomeN1>(last), std::forward<SomeN2>(d_first));
}
template<class It1, class It2>
void do_something(It1 first, It1 second, It2 d_first)
{
copy_helper(std::forward<It1>(first), std::forward<It1>(second), std::forward<It2>(d_first), tag_t<It1>{});
}
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;
}
edited yesterday
answered yesterday
TimoTimo
1,5251626
1,5251626
Thank you, butdo_something
will need to know about theN
namespace, which might be in a disconnected library.
– alfC
yesterday
@alfC Well you could also move the code fromcopy
directly todo_something
and include thecopy_helper
with theno_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement thecopy_helper
with its according tag.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think theforward<It1>
is useless unless you pass arguments asIt1&& first
.
– alfC
yesterday
add a comment |
Thank you, butdo_something
will need to know about theN
namespace, which might be in a disconnected library.
– alfC
yesterday
@alfC Well you could also move the code fromcopy
directly todo_something
and include thecopy_helper
with theno_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement thecopy_helper
with its according tag.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think theforward<It1>
is useless unless you pass arguments asIt1&& first
.
– alfC
yesterday
Thank you, but
do_something
will need to know about the N
namespace, which might be in a disconnected library.– alfC
yesterday
Thank you, but
do_something
will need to know about the N
namespace, which might be in a disconnected library.– alfC
yesterday
@alfC Well you could also move the code from
copy
directly to do_something
and include the copy_helper
with the no_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement the copy_helper
with its according tag.– Timo
yesterday
@alfC Well you could also move the code from
copy
directly to do_something
and include the copy_helper
with the no_tag
tag in the same library. Then you can rely on ADL again. Your disconnected library has to implement the copy_helper
with its according tag.– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
@alfC I edited my answer to rely on ADL.
– Timo
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think the
forward<It1>
is useless unless you pass arguments as It1&& first
.– alfC
yesterday
The Edit is the closest to an answer to the question so far. Note aside, I think the
forward<It1>
is useless unless you pass arguments as It1&& first
.– alfC
yesterday
add a comment |
OK, building on @paler123, but without checking for an existing type, but checking if It1
is a pointer instead:
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);
}
Still C++17, but in the case of pointers, we go through the explicit std::copy
otherwise, we rely on ADL.
In general, your issue is a design problem. You want to use std::copy
for all cases, except for objects from N
, and in that case, you hope that ADL will work. But as you forced std::copy
, you remove the option for proper ADL. You can't have everything and you have to redesign your code.
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implementcopy
for them, even ifstd::copy
would be fine for them.
– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
add a comment |
OK, building on @paler123, but without checking for an existing type, but checking if It1
is a pointer instead:
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);
}
Still C++17, but in the case of pointers, we go through the explicit std::copy
otherwise, we rely on ADL.
In general, your issue is a design problem. You want to use std::copy
for all cases, except for objects from N
, and in that case, you hope that ADL will work. But as you forced std::copy
, you remove the option for proper ADL. You can't have everything and you have to redesign your code.
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implementcopy
for them, even ifstd::copy
would be fine for them.
– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
add a comment |
OK, building on @paler123, but without checking for an existing type, but checking if It1
is a pointer instead:
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);
}
Still C++17, but in the case of pointers, we go through the explicit std::copy
otherwise, we rely on ADL.
In general, your issue is a design problem. You want to use std::copy
for all cases, except for objects from N
, and in that case, you hope that ADL will work. But as you forced std::copy
, you remove the option for proper ADL. You can't have everything and you have to redesign your code.
OK, building on @paler123, but without checking for an existing type, but checking if It1
is a pointer instead:
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);
}
Still C++17, but in the case of pointers, we go through the explicit std::copy
otherwise, we rely on ADL.
In general, your issue is a design problem. You want to use std::copy
for all cases, except for objects from N
, and in that case, you hope that ADL will work. But as you forced std::copy
, you remove the option for proper ADL. You can't have everything and you have to redesign your code.
edited yesterday
answered yesterday
Matthieu BrucherMatthieu Brucher
15k32140
15k32140
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implementcopy
for them, even ifstd::copy
would be fine for them.
– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
add a comment |
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implementcopy
for them, even ifstd::copy
would be fine for them.
– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implement
copy
for them, even if std::copy
would be fine for them.– alfC
yesterday
Thanks for the option. Pointer types are just an example, other (non-std iterators) will not work now. In other words this forces all iterators and pointer-like iterators to implement
copy
for them, even if std::copy
would be fine for them.– alfC
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
Yes, that's the problem, I agree. The problem you have, once again, is a design problem. You want to try to rely on ADL in some cases, but not in others. Not going to work.
– Matthieu Brucher
yesterday
add a comment |
For discussion I will write an answer to my own question with an alternative option.
It is not very nice because it needs to wrap all the N::
classes in another template class (called wrap
here). The good thing is that do_something
nor the N
classes need to know about the special N::copy
. The price is that the main
caller has to explicitly wrap the N::
classes which is ugly but which is fine from the point of view of coupling because this is the only code that should know about the whole system.
#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;
}
add a comment |
For discussion I will write an answer to my own question with an alternative option.
It is not very nice because it needs to wrap all the N::
classes in another template class (called wrap
here). The good thing is that do_something
nor the N
classes need to know about the special N::copy
. The price is that the main
caller has to explicitly wrap the N::
classes which is ugly but which is fine from the point of view of coupling because this is the only code that should know about the whole system.
#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;
}
add a comment |
For discussion I will write an answer to my own question with an alternative option.
It is not very nice because it needs to wrap all the N::
classes in another template class (called wrap
here). The good thing is that do_something
nor the N
classes need to know about the special N::copy
. The price is that the main
caller has to explicitly wrap the N::
classes which is ugly but which is fine from the point of view of coupling because this is the only code that should know about the whole system.
#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;
}
For discussion I will write an answer to my own question with an alternative option.
It is not very nice because it needs to wrap all the N::
classes in another template class (called wrap
here). The good thing is that do_something
nor the N
classes need to know about the special N::copy
. The price is that the main
caller has to explicitly wrap the N::
classes which is ugly but which is fine from the point of view of coupling because this is the only code that should know about the whole system.
#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;
}
answered 13 hours ago
alfCalfC
5,08322959
5,08322959
add a comment |
add a comment |
Suggest you have a look at the very powerful new Boost.HOF library.
This function does exactly what you want:
#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);
}
hof::first_of
will select the first lambda whose return type is deduced to be the result type of a legal expression.
1
This looks like an interesting library. However the problem is thatdo_something
still needs to know about the library/namespaceN
.
– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for callingcopy
.
– Richard Hodges
yesterday
add a comment |
Suggest you have a look at the very powerful new Boost.HOF library.
This function does exactly what you want:
#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);
}
hof::first_of
will select the first lambda whose return type is deduced to be the result type of a legal expression.
1
This looks like an interesting library. However the problem is thatdo_something
still needs to know about the library/namespaceN
.
– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for callingcopy
.
– Richard Hodges
yesterday
add a comment |
Suggest you have a look at the very powerful new Boost.HOF library.
This function does exactly what you want:
#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);
}
hof::first_of
will select the first lambda whose return type is deduced to be the result type of a legal expression.
Suggest you have a look at the very powerful new Boost.HOF library.
This function does exactly what you want:
#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);
}
hof::first_of
will select the first lambda whose return type is deduced to be the result type of a legal expression.
edited yesterday
alfC
5,08322959
5,08322959
answered yesterday
Richard HodgesRichard Hodges
55.6k658100
55.6k658100
1
This looks like an interesting library. However the problem is thatdo_something
still needs to know about the library/namespaceN
.
– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for callingcopy
.
– Richard Hodges
yesterday
add a comment |
1
This looks like an interesting library. However the problem is thatdo_something
still needs to know about the library/namespaceN
.
– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for callingcopy
.
– Richard Hodges
yesterday
1
1
This looks like an interesting library. However the problem is that
do_something
still needs to know about the library/namespace N
.– alfC
yesterday
This looks like an interesting library. However the problem is that
do_something
still needs to know about the library/namespace N
.– alfC
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for calling
copy
.– Richard Hodges
yesterday
@alfC I see. It wasn't clear to me that you're after a general solution for calling
copy
.– Richard Hodges
yesterday
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54400763%2fhow-to-write-a-standard-like-function-that-has-high-overload-priority%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Not a dupe, but related.
– lubgr
yesterday
1
Obviously the error comes from always doing
using std::copy;
.– Matthieu Brucher
yesterday
3
@alfC Your
N::copy
is also a function template that has no arguments associated with namespaceN
. Hence, it is not better thanstd::copy
for the purpose of overload resolution.– Maxim Egorushkin
yesterday
2
@MatthieuBrucher, if I use
std::copy
then there is no chance the special version ofcopy
is called. The only way would be to overloadstd::copy
(in the namespacestd
) which I don't know if it is allowed.– alfC
yesterday
1
@geza, the problem is that
do_something
is a template function that doesn't know about the namespaceN
or the libraryN
.– alfC
yesterday