【问题标题】:SFINAE to enable cast operator only from derived to base classSFINAE 仅启用从派生类到基类的强制转换运算符
【发布时间】:2015-07-22 17:10:34
【问题描述】:

我有一个课程模板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 的无效初始化错误,就好像我的操作员没有被调用一样。

【问题讨论】:

  • 上面写满了poof
  • Q 不在推断的上下文中。但是你到底为什么要这样做呢?
  • @Captain O. 我不明白!
  • @T.C.好吧,我们有这个自制的智能指针类,以及一个非常大的遗留代码库。我们必须手动向下转换 100,000 个位置。我正在对课程进行大修,并认为我会让它含蓄地沮丧。我不希望它隐含向上。我不认为这是一个坏主意。是吗?
  • 我可以看到operator CFoo&lt;Q&gt;() 的用例,尽管使用转换构造函数可能更容易。您目前正在做的事情绝对是个坏主意。 C 风格的演员表是 reinterpret_cast 加上 const_cast,提示,提示...

标签: c++ templates c++11 template-argument-deduction


【解决方案1】:

根据[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;

这很简单,我们尝试根据CFoo&lt;A&gt;&amp; 推断出CFoo&lt;Q&gt;&amp;。该部分还有其他规则,但最终以Q == A 成功演绎。

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

template <class Q>
operator typename std::enable_if<1, CFoo<Q>&>::type const;

在这里,我们试图推断typename std::enable_if&lt;1, CFoo&lt;Q&gt;&amp;&gt;::type。这是一个非推导上下文(它是使用 qualified-id 指定的类型的 nested-name-specifier),因此推导失败。所以不会考虑这个转换函数,所以赋值失败,因为没有找到转换。

你需要返回类型是推导的上下文,所以 SFINAE 必须到这里:

template <class Q,
          typename = std::enable_if_t<std::is_base_of<Q, T>::value>>
operator CFoo<Q>& const;

这样,我们有一些东西要推导 (CFoo&lt;Q&gt;&amp;) - 并且推导可以成功(如果 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

您正在构造一个必然有效的 new 对象。

【讨论】:

  • 你摇滚!现在,你能修复 c++,所以在 c++20 版本中,我们采用了这个很棒的模板过滤功能并使其变得简单。
  • @johnnycrash 什么是 C++20?
  • 这解决了推论问题,但 OP 的代码更大的问题是几乎任何使用返回的引用都会导致 UB。
  • @Barry,c++20 还不存在,除了在我的脑海中。它是 c++ 扩展和简化 SFINAE 的地方。
  • 在 C++1z 中,我们有template&lt;class Q&gt; requires std::is_base_of&lt;T,Q&gt;{}() CFoo(CFoo&lt;Q&gt; const&amp;) {},它比上面的转换构造函数示例稍微清晰。
猜你喜欢
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 2015-03-27
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多