Define friend function template of class template
Asked Answered
K

1

4

I want to define a function template of a class template. The code looks like this.

template<int M>
struct test{
private:
    int value;

    template<int N = 2 * M>
    friend auto foo(test const t){
        test<N> r;
        r.value = t.value;
        return r;
    }
};

int main(){
    test<1> t;
    foo(t);// expected to return test<2>
    foo<1>(t);// expected to return test<1>
    foo<3>(t);// expected to return test<3>
}

But it won't compile. Compared with previous problems, the following lists the differences.

  1. The result of the function template involves another instatiation of the class template. It seems that the function template has to be defined outside. But I am not sure.
  2. The function template uses default template arguments. So, if the function template is defined outside the class, a helper function template is required.

Compiling Errors with g++ -std=c++1z:

a.cpp: In instantiation of 'auto foo(test<M>) [with int N = 2; int M = 1]':
a.cpp:16:10:   required from here
a.cpp:4:9: error: 'int test<2>::value' is private
     int value;
         ^
a.cpp:9:17: error: within this context
         r.value = t.value;
                 ^
a.cpp: In instantiation of 'auto foo(test<M>) [with int N = 3; int M = 1]':
a.cpp:18:13:   required from here
a.cpp:4:9: error: 'int test<3>::value' is private
     int value;
         ^
a.cpp:9:17: error: within this context
         r.value = t.value;
                 ^

A possible workaround, yet not correct either.

template<int M>
struct test{
private:
    int value;

    template<int NA, int NR>
    friend test<NR> foo_impl(test<NA> const&);
};

template<int NA, int NR>
test<NR> foo_impl(test<NA> const& t){
    test<NR> r;
    r.value = t.value;
    return r;
}

template<int NR, int NA>
auto foo(test<NA> const& t){
    return foo_impl<NA, NR>;
}

template<int NA>
auto foo(test<NA> const& t){
    return foo_impl<NA, NA * 2>(t);
}

int main(){
    test<1> t;
    foo(t);
    foo<3>(t);
    foo<1>(t);
}

Error:

t.cpp: In function 'int main()':
t.cpp:31:13: error: call of overloaded 'foo(test<1>&)' is ambiguous
     foo<1>(t);
             ^
t.cpp:18:6: note: candidate: auto foo(const test<NR>&) [with int NR = 1; int NA = 1]
 auto foo(test<NA> const& t){
      ^
t.cpp:23:6: note: candidate: auto foo(const test<NA>&) [with int NA = 1]
 auto foo(test<NA> const& t){
      ^
Klotz answered 17/6, 2016 at 12:51 Comment(1)
When asking why something won't compile, it's generally nice to include the compiler error.Astronavigation
P
6

After your edit, one possible way (which may not be better than your workaround - but work for all call) would be to use a default value for the parameter N:

template<int M>
struct test{
private:
    int value;

    template<int K, int U, int P>
    friend test<P> foo(test<U> const t);

};

template <int N = 0, int M, int P = ((N == 0) ? M * 2 : N)>
test<P> foo (test<M> const t) {
  test<P> r;
  r.value = t.value;
  return r;
}

int main(){
    test<1> t;
    test<2> p1 = foo(t);
    test<3> p2 = foo<3>(t);
    test<1> p3 = foo<1>(t);
}

This may not be prettier than your version...


The problem here is that you are declaring foo<N> a friend of test<M> while you want it to be a friend of any test<...>. You should do the following:

template<int M>
struct test{
private:
    int value;

    template<int U, int K>
    friend test<K> foo(test<U> const t);
};

template <int M, int N = 2 * M>
test<N> foo (test<M> const t) {
    test<N> r;
    r.value = t.value;
    return r;
}

int main(){
    test<1> t;
    foo(t);
}

Here you are saying:

Any function test<K> foo<U, K> (test<U> const) is a friend of any test<...>.

Precursory answered 17/6, 2016 at 13:1 Comment(3)
I add another call in the main, and as a result, the tempalte argument of return type should be the first.Klotz
@Klotz See my edit, but I am not sure you can do something pretty in this case (my version is not really prettier than yours... ).Precursory
@Klotz After your last edit, my code works in all (your) test cases, there is no ambiguous call, you simply cannot call foo<0> - But you could replace the default value with anything that match your requirement.Precursory

© 2022 - 2024 — McMap. All rights reserved.