我有一个类模板CFoo<T>。我想允许对CFoo的其他实例进行隐式转换,但仅适用于其模板参数是T的基类的对象。

我尝试使用SFINAE,但是我的任何尝试都不能在我尝试过的任何编译器上使用(VC 2012或gcc):

#include <type_traits>

template <class T> class CFoo {
public:
    template <class Q>  operator
     // typename std::enable_if<std::is_base_of<Q, T>::value, CFoo<Q>&>::type   // SHOULD WORK?
     // typename std::enable_if<1, CFoo<Q>&>::type                              // SHOULD WORK?
     CFoo<Q>&  // compiles, but doesn't restrict on Q like I want
     () const {
        return *(CFoo<Q>*)this;
    }
};

class A {};
class B : public A {};

int main(int argc, char* argv[])
{
    CFoo<B> b;
    CFoo<A>& a = b;
    return 0;
}

为什么对SFINAE的任何评论都没有在这里起作用?在这两种情况下,我都因为a的无效初始化而收到错误,好像没有调用我的运算符。

最佳答案

根据[temp.deduct.conv]:



在简单的情况下:

template <class Q>
operator CFoo<Q>& const;

很简单,我们尝试根据CFoo<Q>&推导CFoo<A>&。该部分中还有其他规则,但最终推论成功了Q == A

您的其他两次尝试均因相同的原因而失败。我将选择较简单的一个:
template <class Q>
operator typename std::enable_if<1, CFoo<Q>&>::type const;

在这里,我们试图推导typename std::enable_if<1, CFoo<Q>&>::type。这是一个非推论上下文(它是使用qualified-id指定的类型的嵌套名称说明符),因此推论失败。因此将不考虑此转换功能,因此分配失败,因为未找到转换。

您需要将返回类型设为推论上下文,因此SFINAE必须转到此处:
template <class Q,
          typename = std::enable_if_t<std::is_base_of<Q, T>::value>>
operator CFoo<Q>& const;

这样,我们就可以推断出一些东西(CFoo<Q>&)-并且推论可以成功(如果QT的基础):
CFoo<A>& a = b; // OK
CFoo<int>& i = b; // deduction failure on Q, so there's no viable conversion function
                  // so this is an error

就是说,虽然我很着迷于解决模板难题,但T.C.指出,这确实不是一个好的解决方案,因为:
return *(CFoo<Q>*)this;

只是做了一个reinterpret_cast(和一个const_cast),所以它实际上不可能做任何合理的事情,并且您几乎可以肯定(除非CFoo是琐碎的事情)通过尝试使用错误的类型访问其成员而导致未定义的行为。

您可能要添加一个转换构造函数而不是一个转换函数:
template <typename Q,
          typename = std::enable_if_t<std::is_base_of<T, Q>::value>>
CFoo(CFoo<Q> const& ) { }

这样,当您执行以下操作时:
CFoo<A> a = b; // a is not a reference anymore

您正在构造一个必须有效的新对象。

10-08 08:48