简体   繁体   中英

Incomplete type used in nested name specifier with std enable_if

I have that piece of code (which is a minimal reproduced from a way larger project).

#include <type_traits>    
template<typename PA, typename E = void>
struct poly_gcd_reduce_helper;

template<typename PA>
struct poly_gcd_reduce_helper<PA, typename std::enable_if<(PA::sign() > 0)>::type>
{
    static constexpr auto val = PA{};
};

template<typename PA>
struct poly_gcd_reduce_helper<PA, typename std::enable_if<(PA::sign() <= 0)>::type>
{
    static constexpr auto val = -PA{};
};

template<typename PA, typename PB>
struct poly_gcd 
{
    static constexpr auto val = poly_gcd<PB, decltype(PA{} -(PA{} / PB{}) * PB {})>::val;
};

template<typename PA>
struct poly_gcd<PA, typename PA::zero_type> 
{
    static constexpr auto val = poly_gcd_reduce_helper<PA>::val;
};

template<int p>
struct myint{
    static constexpr int val = p;
    using zero_type = myint<0>;
    constexpr int sign() const {
        if constexpr (p > 0)
            return 1;
        else if constexpr (p == 0)
            return 0;
        else
            return -1;
    }

    constexpr auto operator-() const {
        return myint<-p>{};
    }
};

template<int a, int b>
static constexpr auto operator/(myint<a> aa, myint<b> bb)
{
    return myint<a / b>{};
}
template<int a, int b>
static constexpr auto operator*(myint<a> aa, myint<b> bb)
{
    return myint<a * b>{};
}
template<int a, int b>
static constexpr auto operator-(myint<a> aa, myint<b> bb)
{
    return myint<a - b>{};
}
template<int a, int b>
static constexpr auto operator+(myint<a> aa, myint<b> bb)
{
    return myint<a + b>{};
}

int main() {
    constexpr auto zou = poly_gcd<myint<2>, myint<4>>::val;
}

It fails with gcc 9.2 with the following error :

In instantiation of 'constexpr const auto poly_gcd, myint<0>>::val':
recursively required from 'constexpr const auto poly_gcd, myint<2> >::val'
required from 'constexpr const auto poly_gcd, myint<4> >::val'
:70:56: required from here
:27:27: error: incomplete type
'poly_gcd_reduce_helper, void>' used in nested name specifier

static constexpr auto val = poly_gcd_reduce_helper::val;

Apparently the compiler tries to instanciate poly_gcd_reduce_helper, void> even if I have mutually exclusive implementations of poly_gcd_reduce_helper depending on the sign of the first template argument.

I must admit I don't know what to do now.

In the specialization of poly_gcd_reduce_helper , given the usage PA::sign() > 0 , sign() is expected to be a static member function; while myint::sign() is a non-static member function. Then for myint the specialization of poly_gcd_reduce_helper would never be selected.

Change it to

static constexpr int sign() {
    if constexpr (p > 0)
        return 1;
    else if constexpr (p == 0)
        return 0;
    else
        return -1;
}

LIVE

There's no need to wrap an integer in a special type, this can all be dramatically simplified:

#include <iostream>
#include <type_traits>

using namespace std;

template<int VA, int VB>
struct poly_gcd { 
    static constexpr auto val = poly_gcd<VB, VA-(VA/VB)*VB>::val;
};

template<int VA>
struct poly_gcd<VA, 0> {
    static constexpr int val = (VA < 0) ? -VA : VA;
};

int main() {
    constexpr auto zou = poly_gcd<5,10>::val;
    cout << "zou: " << zou << endl;
}

╰─▸ ./test
zou: 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM