Conditionally compile a conversion operator based

2019-07-24 15:28发布

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);
    }
}

3条回答
Explosion°爆炸
2楼-- · 2019-07-24 16:20

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

查看更多
Luminary・发光体
3楼-- · 2019-07-24 16:23

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

查看更多
我想做一个坏孩纸
4楼-- · 2019-07-24 16:29

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.

查看更多
登录 后发表回答