5

I have a template function in the style:

template <int Exponent> DERIVED_TYPE pow(TYPE const x);

This function is defined inline in a template struct as a friend function:

template <ARGUMENTS>
struct unit {
    typedef unit<ARGUMENTS> type;

    ....
    template <int Exponent>
    friend constexpr unit_pow_t<type, Exponent> pow(type const x) { ... }
};

This is because taking a value with a unit to a power has to change the unit along with the value.

When I try to use it omitting the Exponent, I can see the candidates the compiler considers for matching:

src/model/Tool.cpp:113:3: error: no matching function for call to 'pow'
                pow(1._m);
                ^~~
src/model/../units/Units.hpp:2266:46: note: candidate template ignored: couldn't infer template argument 'Exponent'
        friend constexpr unit_pow_t<type, Exponent> pow(type const x) {
                                                    ^
/usr/include/math.h:255:8: note: candidate function not viable: requires 2 arguments, but 1 was provided
double  pow(double, double);
        ^

So far things are as expected, the template is seen, but of course the Exponent needs to be specified. When I do however something unexpected happens:

src/model/Tool.cpp:113:6: error: comparison between pointer and integer ('double (*)(double, double)' and 'int')
                pow<3>(1._m);
                ~~~^~

The compiler sees pow as the address of "double pow(double, double)" and interprets <3 as the intent to compare the function pointer with the integer. The problem occurs with clang 3.4, 3.6 and GCC 5.2.

My question is, how do I convince the compiler that <3> is a template argument list?

UPDATE

I finally managed to create a minimal example, sorry for the incomplete question:

template <int Exp>
struct metre {
    double value;
    template <int Exponent>
    friend constexpr metre<Exp * Exponent> pow(metre<Exp> const x) {
        return {0};
    }
};

int main() {
    pow<2>(metre<1>{1});
    return 0;
};

It seems it is not seeing pow:

targs.cpp:11:2: error: use of undeclared identifier 'pow'
        pow<2>(metre<1>{1});
        ^

If I include cmath I have the same diagnostics as before:

targs.cpp:13:5: error: comparison between pointer and integer ('double (*)(double, double)' and 'int')
        pow<2>(metre<1>{1});
        ~~~^~
1 error generated.

So the presence of "double pow(double, double)" just masks the issue that the template is not seen. The question then is, why is pow<>() not seen by the compiler?

kamikaze
  • 1,529
  • 8
  • 18
  • 2
    Please post an [MVCE](http://stackoverflow.com/help/mcve). – TartanLlama Dec 10 '15 at 09:30
  • @TartanLlama Didn't manage to create one. I probably need to understand the cause for my problem to create one, making asking the question obsolete. – kamikaze Dec 10 '15 at 09:40
  • 2
    @kamikaze: exactly. We can't help you if you don't post the code causing the error in the first place. – rubenvb Dec 10 '15 at 09:51
  • @TartanLlama Thank you for the motivation, I now managed to create an MVCE. I was thinking way to complicated, due to the complexity of the underlying classes. – kamikaze Dec 10 '15 at 10:02
  • Now comes the ever important question of what it is this code is supposed to accomplish. As it stands, the code makes little sense. You are calling C's `double pow(double, double)` function when including ``. You want `metre::pow` but you can't do that because it needs a template parameter. This is where the code stops making any sense to me. – rubenvb Dec 10 '15 at 10:11
  • I would suggest making a new question entitled something like "Friend template function of template class not found" or something like that. Template friends are notoriously difficult to work with. – TartanLlama Dec 10 '15 at 10:34
  • @rubenv pow<2>(metre<1>) is supposed to return a square metre (metre<2>). – kamikaze Dec 10 '15 at 10:55
  • 1
    http://stackoverflow.com/a/8284809/1467466 (and, iirc, ADL is limited between templated and non-templated functions) – user Dec 10 '15 at 11:04
  • @TartanLlama I guess it's a question of what is more helpful to have in the title. The actual problem or the diagnostic people are likely to see. – kamikaze Dec 11 '15 at 10:05

3 Answers3

3

There is no need (and no logic) for the friend template. Use a free function:

template <int Exp>
struct metre {
    double value;
};

template<int B, int A>
constexpr auto pow(metre<A> const x) -> metre<A*B>
{
        return metre<A*B>{x.value};
}

int main() {
    metre<2> result = pow<2>(metre<1>{1});
    return 0;
};

Live demo on coliru.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • 1
    This doesn't answer the question. – user Dec 10 '15 at 11:05
  • If anyone insists on having friend template function, then one can add the friend declaration (without inline implementation). The confusing bit here is that even declaration needs two template arguments. – mariusm Dec 10 '15 at 11:20
  • @user well - it provides a workaround, which is something. It's fairly complicated to explain the problem in the original code! – M.M Dec 10 '15 at 11:29
  • @M.M I get that, but asserting that a particular approach is irrelevant and leaving it at that when there exists a satisfactory explanation is a pet peeve of mine; I prefer the spread of knowledge over dogma (and I see the twinge of irony in my earlier response in spite of that :-P ). – user Dec 10 '15 at 11:49
  • For the MVCE there is no reason to use an inline defined friend template. It looks differently in my actual use case, where it is possible to define a template function that does the same outside of class scope, but it is ugly and incomprehensible in comparison to the inline version. – kamikaze Dec 10 '15 at 12:46
  • @kamikaze I don't see how either of the two would be more or less comprehensible unless you're either not telling the whole story or you're doing something the hard way. – rubenvb Dec 10 '15 at 12:50
  • Defining the function inline spares me about a million **typename Unit::trait_name**. – kamikaze Dec 10 '15 at 13:02
  • So your `Unit` classes have no copy constructors? I'm assuming here that by `trait_name` you actually mean `member`. Traits are by convention something else in C++, see e.g. the [`` header](http://en.cppreference.com/w/cpp/header/type_traits). – rubenvb Dec 10 '15 at 13:09
  • And anyways, I don't see the issue... http://coliru.stacked-crooked.com/a/da22d2cfa2feb52c – rubenvb Dec 10 '15 at 13:33
3

This is the template friends problem, with two quirks: the name of the function being pow; and the template friend has its own template parameter!

As a rule of thumb, be on guard whenever you use friend inside a class template.

To get the easier problem out of the way first: as posted, in the MCVE, the class definition of metre doesn't cause a name pow to be declared (this will be explained later). Your error message is coming because there is in fact a visible declaration of a name pow: it's in the C standard library header math.h. The "pointer" in the error message is a function pointer to this function.

It's a good idea to not name your function the same as a function in the C standard library. Those may be defined as preprocessor macros anyway, causing further trouble.

For the rest of this post I will assume the MCVE has pow swapped out for pok , to avoid this wrinkle. Then a sane error message is generated:

po.cc:13:5: error: 'pok' was not declared in this scope
    pok<2>(metre<1>{1});
    ^

Moving onto the main issue now.

The basic version of the problem is discussed here. The issue is that declaring a friend function inside a class template does NOT make the friend function also be a template function with the same parameter as the class template.

In fact, what happens is that for each instantiation of the class template, the friend declaration declares a non-template friend for that instantiation.

To see this in action, in your MCVE, add the line metre<1> m; as the first line of main. This instantiates metre<1>, which causes template<int Exponent> pok(metre<1>) to exist, and so the compiler recognizes pok on the next line!

In fact, any particular instantiation works, not just metre<1>, because this at least allows name lookup of pok to succeed, and then by the time overload resolution occurs (two-phase lookup!) , the argument metre<1> has caused metre<1> to be instantiated.


Closing note: I'm not entirely sure the above explanation is correct - template friends are pretty complicated. Maybe it'll turn out that actually pok is supposed to be declared and the compiler is bugged. But I concur with rubenvb's suggestion that it is best to avoid this situation entirely by not using a friend.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Does this mean that explictly instantiating `metre` like `template struct metre<0>;` after the class definition is a portable solution to this error? (Not that it's a *good* solution, totally agreed that avoiding template friends is the best option) – TartanLlama Dec 10 '15 at 11:48
  • @TartanLlama that solution appears to work for me (although as I say in my closing note, I'm trusting the compiler rather than reading the standard which is frightfully complicated in this area) – M.M Dec 10 '15 at 11:52
0

With the help of the previous answers I figured out a workaround.

The problem is that the friends are defined (more or less as expected) but not known outside of class/struct scope. So the compiler does not find them.

The solution is to put a declaration outside of the struct:

template <int Exp>
struct metre {
    double value;

    template <int Exponent>
    friend constexpr metre<Exp * Exponent> pow(metre<Exp> const x) {
        return {23};
    }
};

template <class> void pow(); // <== HERE BE DRAGONS

int main() {
    static_assert(pow<2>(metre<1>{1}).value == 23, "foobar");
    return 0;
};

Note that it is not necessary to provide a matching declaration. Any template declaration for a function with the right name seems to allow the compiler to discover the friend template function.

kamikaze
  • 1,529
  • 8
  • 18
  • Do you even know what dark corner (well, it's not so dark really) of the language you're using? – rubenvb Dec 10 '15 at 15:49
  • @rubenvb I guess it works similar to the 'fix' in my answer: it enables name lookup for `pow` – M.M Dec 10 '15 at 19:51
  • The key problem with this, that so far we've all failed to say, is 'maintainability'. Koenig lookup (which this depends on) isn't widespread knowledge, so if you were writing code for a company, you just screwed over your maintainability for little gain. More concretely: the next programmer to touch your code will, in all likelihood, be a sad panda trying to tolerate what you just wrote. – user Dec 12 '15 at 12:02
  • Fair enough, in the end I decided to make this particular one a member function. Still I wanted to know how it can be done. – kamikaze Dec 14 '15 at 10:24