Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler mistakes template argument list for < comparison

Tags:

c++

templates

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?

like image 387
kamikaze Avatar asked Dec 10 '15 09:12

kamikaze


1 Answers

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.

like image 167
rubenvb Avatar answered Nov 15 '22 06:11

rubenvb