Conditionally compile a conversion operator based on numeric template parameter

早过忘川 提交于 2019-12-11 01:18:26

问题


I have a template <bool P> class Foo with lots of code in it. I want to be able to convert Foo<true>'s into Foo<false>'s, i.e. have an operator Foo<false>() method. But the compiler doesn't like such a method existing for Foo, it only likes it for Foo<true>, and gives a warning about how the "operator will not be called for implicit or explicit conversions" (GCC 5.4.x)

It doesn't seem like I can use SFINAE for this: std::enable_if works with types; and a value-variant I tried out (where the true case has a value rather than a type member) didn't help either.

How can I get this operator to only be compiled for Foo<false> (other than specializing Foo<false> different and duplicating all of my code)?

My best attempt so far has been:

template <bool P> class Foo {
    // etc. etc.
    template <bool OtherValue>
    operator Foo<OtherValue>()
    {
        static_assert(OtherValue, "You should not be using this conversion!");
        // conversion code here
        return Foo<false>(args,go,here);
    }
}

回答1:


How can I get this operator to only be compiled for Foo<false> (other than specializing Foo<false> different and duplicating all of my code)?

template <bool P> 
struct Foo 
{
    template <bool P2 = P, typename = std::enable_if_t<P2 == true>>
    operator Foo<false>()
    {
        return {};
    }
};

Using P2 = P delays the evaluation of the enable_if_t to when the conversion operator is actually being used (instead of class instantiation).


If we tried to simply write:

template <typename = std::enable_if_t<P == true>>
operator Foo<false>()
{
    return {};
}

std::enable_if_t<P == true> would be evaluated during Foo<P>'s instantiation as there is no substitution occurring when the member functions are instantiated. The substitution is happening when Foo is instantiated - therefore SFINAE cannot take place (as there isn't any overload resolution set yet).

By adding a bool P2 = P default parameter, we delay substitution to the instantiation of the conversion operator. This happens during overload resolution, so SFINAE can take place.

This answer explains it better than I can: https://stackoverflow.com/a/13401982/598696




回答2:


Your best attempt is actually not far off. You need to turn the conversion operator into a template so that SFINAE could work. Just enforce it only for the case P is true:

template<bool P>
struct foo {
    template<bool V = P, std::enable_if_t<V>* = nullptr>
    operator foo<false>() {
        return {};
    }
};

The operator is a template with all parameters having default arguments. So it can be used. However, since the check is on V, we delay the verification of the operator until overload resolution. Then SFINAE eliminates it.




回答3:


You might use an helper struct and specilization to externalize your operator:

template <bool P> class Foo; // Forward declaration

template <bool P> struct FooConverterHelper {}; // False case: no conversion operator

template <> struct FooConverterHelper<true>
{
    operator Foo<false>();
};


template <bool P> class Foo : public FooConverterHelper<P>
{
    // Lot of stuff.
};

// Implementation of you conversion operator
FooConverterHelper<true>::operator Foo<false>()
{
    //auto* that = static_cast<Foo<true>*>(this); // If needed

    return Foo<false>{};
}

Demo



来源:https://stackoverflow.com/questions/46907372/conditionally-compile-a-conversion-operator-based-on-numeric-template-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!