22

When I pass a template function as a template parameter of a base class, the linker complains that it cannot link the function:

#include <stdio.h>

template<int I> inline int identity() {return I;}
//template<> inline int identity<10>() {return 20;}

template<int (*fn)()>
class Base {
public:
    int f() {
        return fn();
    }
};

template<int Val>
class Derived : public Base<identity<10> > {
public:
    int f2() {
        return f();
    }
};

int main(int argc, char **argv) {
    Derived<10> o;
    printf("result: %d\n", o.f2());
    return 0;
}

Results in:

$ g++ -o test2 test2.cpp && ./test2
/tmp/ccahIuzY.o: In function `Base<&(int identity<10>())>::f()':
test2.cpp:(.text._ZN4BaseIXadL_Z8identityILi10EEivEEE1fEv[_ZN4BaseIXadL_Z8identityILi10EEivEEE1fEv]+0xd): undefined reference to `int identity<10>()'
collect2: error: ld returned 1 exit status

If I comment out the specialization, then the code compiles and links as expected. Also, if I inherit from Base<identity<Val> > instead of Base<identity<10> >, the code works as I expect.

Try here: http://coliru.stacked-crooked.com/a/9fd1c3aae847aaf7

What do I miss?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ferenc
  • 779
  • 1
  • 6
  • 14
  • 3
    This problem seems to be a gcc bug: it compiles and links OK using clang and icc. BTW, the name _identity()_ is normally used for transformation where the result is identical to the argument. – Dietmar Kühl Dec 30 '16 at 16:04
  • @DietmarKühl Well, `identity()` returns `X`. :-) – melpomene Dec 30 '16 at 16:05
  • 2
    The workaround: `class Derived : public Base(identity<10>) >`. [live demo](http://melpon.org/wandbox/permlink/E4aRHqcZaac7vd3C) – W.F. Dec 30 '16 at 16:07
  • @melpomene: sure. However, the template parameter seems to be more something like an index (as in fi()) than a function argument. – Dietmar Kühl Dec 30 '16 at 16:07

2 Answers2

19

It seems the problem is a gcc error: the code compiles and links with clang, icc, and the EDG frontend. A potential work-around not changing any of the uses would be the use of a class template identity instead of a function:

template<int I>
struct identity {
    operator int() { return I; }
};

template<typename fn>
class Base {
public:
    int f() {
        return fn();
    }
};
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Not making the function template `inline` could also work. Isn't linker supposed to remove duplicate template instantiations anyway? – Etherealone Jan 03 '17 at 18:30
9

Lifting it out into a typedef makes it compile, i.e.

typedef Base< identity<10> > base10;

I am not quite sure why doing it straight in the class definition doesn't work.

http://coliru.stacked-crooked.com/a/f00b4f4d1c43c2b0

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Biggy Smith
  • 910
  • 7
  • 14