SFINAE只能从派生到基类启用强制转换操作符

joh*_*ash 2 c++ templates c++11 template-argument-deduction

我有一个课程模板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;
}
Run Code Online (Sandbox Code Playgroud)

为什么SFINAE的任何评论尝试都没有在这里工作?在这两种情况下,我只是因为无效的初始化而得到错误a,好像我的操作符没有被调用.

Bar*_*rry 7

根据[temp.deduct.conv]:

模板参数推导是通过将转换函数模板的返回类型(称为P)与转换​​结果所需的类型进行比较来完成的(称之为A;请参见8.5,13.3.1.5和13.3.1.6确定该类型)如14.8.2.5所述.

在简单的情况下:

template <class Q>
operator CFoo<Q>& const;
Run Code Online (Sandbox Code Playgroud)

这是简单的,我们试图推断CFoo<Q>&反对CFoo<A>&.该部分还有其他规则,但最终扣除成功Q == A.

由于同样的原因,您的其他两次尝试都失败了.我会选择更简单的一个:

template <class Q>
operator typename std::enable_if<1, CFoo<Q>&>::type const;
Run Code Online (Sandbox Code Playgroud)

在这里,我们试图推断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;
Run Code Online (Sandbox Code Playgroud)

这样,我们有一些东西可以演绎(CFoo<Q>&) - 并且演绎可以成功(如果Q是基础T):

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
Run Code Online (Sandbox Code Playgroud)

也就是说,当我解决模板难题时,正如TC指出的那样,这真的不是一个好的解决方案,因为:

return *(CFoo<Q>*)this;
Run Code Online (Sandbox Code Playgroud)

只做一个reinterpret_cast(和一个const_cast)所以它真的不可能做任何合理的事情,你几乎肯定(除非CFoo是微不足道)通过尝试访问其错误类型的成员最终导致未定义的行为.

您可能想要添加转换构造函数而不是转换函数:

template <typename Q,
          typename = std::enable_if_t<std::is_base_of<T, Q>::value>>
CFoo(CFoo<Q> const& ) { }
Run Code Online (Sandbox Code Playgroud)

这样,当你这样做时:

CFoo<A> a = b; // a is not a reference anymore
Run Code Online (Sandbox Code Playgroud)

您正在构建一个必然有效的对象.

  • 这解决了演绎问题,但OP代码的更大问题是几乎任何使用返回的引用都会导致UB. (2认同)