【问题标题】:C++ templates and ambiguity problemC++模板和歧义问题
【发布时间】:2009-12-13 21:32:19
【问题描述】:

我有一个指针类的子集,如下所示:

template <typename T>
struct Pointer
{
     Pointer();
     Pointer(T *const x);
     Pointer(const Pointer &x);
     template <typename t>
     Pointer(const Pointer<t> &x);

     operator T *() const;
};

最后一个构造函数的目标是允许传递子类的Pointer,或者基本上任何可隐式转换为T * 的类型。这个实际规则仅由构造函数的定义强制执行,编译器实际上无法仅通过声明来计算它。如果我放弃它,并尝试将Pointer&lt;Sub&gt; 传递给Pointer&lt;Base&gt; 的构造函数,我会得到一个编译错误,尽管可能通过operator T *() 的路径。

虽然它解决了上述问题,但它创造了另一个问题。如果我有一个重载函数,其一个重载采用Pointer&lt;UnrelatedClass&gt;,另一个采用Pointer&lt;BaseClass&gt;,并且我尝试使用Pointer&lt;SubClass&gt; 调用它,我会在这两个重载之间产生歧义,当然,其目的是后一个重载将被调用。

有什么建议吗? (希望我足够清楚)

【问题讨论】:

  • 使用大写字母U 作为附加模板参数更为常见。小写的t 几乎看起来像是一个错字。
  • 隐式转换是个坏主意。使用T * get() 而不是operator T*()。 shared_ptr 做这样的事情。

标签: c++ templates ambiguity implicit-conversion ambiguous-call


【解决方案1】:

解决您问题的方法称为 SFINAE(替换失败不是错误)

#include "boost/type_traits/is_convertible.hpp"
#include "boost/utility/enable_if.hpp"

template<typename T>
class Pointer {
   ...
   template<typename U>
   Pointer(const Pointer<U> &x,
      typename boost::enable_if<
         boost::is_convertible<U*,T*>
      >::type* =0)
   : ...
   {
     ...
   }
   ...
};

如果 U* 可转换为 T*,enable_if 将有一个 typedef 成员 type 默认为 void。然后,一切都很好。如果 U* 不能转换为 T*,则缺少此 typedef 成员,替换失败并忽略构造函数模板。

这解决了您的转换和歧义问题。

回复评论:is_convertible 看起来像这样:

typedef char one;         // sizeof == 1  per definition
struct two {char c[2];};  // sizeof != 1

template<typename T, typename U>
class is_convertible {
    static T source();
    static one sink(U);
    static two sink(...);
public:
    static const bool value = sizeof(sink(source()))==1;
};

【讨论】:

  • 明白了,好主意。该项目目前不使用 boost。知道is_convertible 是如何工作的吗?
【解决方案2】:

尝试使有问题的构造函数显式,例如:

 template <typename t>
 explicit Pointer(const Pointer<t> &x);

和/或删除operator T *() const; - 我认为这也会造成歧义。

编辑

检查std::auto_ptr 界面,并与您的进行比较。至少他们解决了歧义。

【讨论】:

  • explicilt 添加到声明中与完全删除它具有相同的效果:它会产生编译错误(2 个重载都不能转换所有参数类型...)。删除强制转换运算符并不能解决歧义(无论如何它是该类的一个有价值的部分)。
  • 您请添加代码,使用Pointer的位置,并请添加(作为注释)发生编译错误的位置,以及哪个错误..
  • 虽然强制转换操作符仍然可能是歧义的原因(正如 Alexey 已经指出的那样),但隐式转换是一个坏主意:std::auto_ptr 没有添加它,而 boost ptrs 有出于同样的原因不要添加它(在某些情况下会出现歧义)。
猜你喜欢
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多