C ++使用模板参数来解决重载问题

时间:2017-08-15 19:56:12

标签: c++ templates overloading implicit-conversion template-meta-programming

我正在编写一个包装器模板类,它可以包装任意类型并使用一些额外的语义来填充它,但我无法弄清楚如何使重载决策正常工作。当通常通过比较竞争转换序列的排名来解决的转换不能由编译器推断出因为所讨论的类型是模板参数而不是函数参数时,就会出现问题。例如,

#include <type_traits>

template <typename T> class Wrapper {
  T val;

public:
  Wrapper() = default;
  template <typename U> Wrapper(Wrapper<U> x) : val(x.val) {}
};

void foo(Wrapper<const char *>) {}
void foo(Wrapper<bool>) {}

int main() {
  Wrapper<char *> cp;
  foo(cp);
}

这里,对foo()的调用是不明确的。所需的行为是编译器选择void foo(Wrapper<const char *>),如果cp而不是char *foo则是void foo(const char *)。这可能吗?

编辑:感谢大家的快速回复,但也许我应该更加清楚。我上面给出的只是一个例子。我需要的是对以下问题的一般解决方案:给定任意类型TUV,假设C ++的内置重载解析更倾向于转换T - &GT; U超过T - &gt; V。那我怎样才能确保C ++更喜欢Wrapper<T> - &gt; Wrapper<U>超过Wrapper<T> - &gt; Wrapper<V>

我做了这个澄清,因为似乎答案是专门解决重载解析的某些方面,比如cv-qualifiedness,而我真的需要一个通用的解决方案。

4 个答案:

答案 0 :(得分:1)

你需要让构造函数不那么贪心。这可以通过SFINAE

完成
template <typename T>
using remove_const_from_pointer_t =
  std::conditional_t<std::is_pointer<T>::value,
    std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>, T>;

template <typename T>
class Wrapper {
  T val;

  template <typename U>
  friend class Wrapper;

public:
  Wrapper() = default;

  template <
    typename U,
    std::enable_if_t<
      std::is_same<U, remove_const_from_pointer_t<T>>::value, int*> = nullptr>
  Wrapper(Wrapper<U> x) : val(x.val) {}
};

您可能想尝试this而不是remove_const_from_pointer_t

另请注意,我必须添加friend声明。

修改:如果只有一个void foo(Wrapper<bool>)超载,这不起作用,您必须从Wrapper&#39移动SFINAE的应用程序; s构造函数直接到这个重载:

template <
  typename T, 
  std::enable_if_t<
    std::is_same<std::remove_const_t<T>, char>::value, int*> = nullptr>
void foo(Wrapper<T *>) { }

答案 1 :(得分:1)

这里的问题是,由于模板,两个重载在分辨率上具有完全相同的权重。

如果要进行重载解析,则必须引入重载解析 这可以通过将相应的类型添加为第二个(未使用的)参数来完成:

void foo(Wrapper<const char *>, const char *)
void foo(Wrapper<bool>,         bool)

借助包装器中的以下别名:

using value_type = T;

以下foo()函数可以选择最佳重载:

template <typename W> 
void foo(W && w) {
  foo(std::forward<W>(w), typename std::remove_reference_t<W>::value_type{}); 
}

DEMO

答案 2 :(得分:0)

您在主要的const前面缺少char*

如下所述声明。它应该工作。

Wrapper<const char *> cp;

以下是测试和结果

http://rextester.com/FNOEL65280

答案 3 :(得分:0)

你可以做的事情很少:

  1. 简单地禁止构造Wrapper&lt; bool&gt;来自Wrapper&lt; T *&gt;。这样的事情很容易出错
  2. 使用SFINAE
  3. #include <type_traits>
    
    template <typename T> class Wrapper {
      T val;
    public:
      T getVal() const {
        return val;
      }
      Wrapper() = default;
      template <typename U,
                class = typename std::enable_if<std::is_same<typename std::remove_cv<typename std::remove_pointer<T>::type>::type,
                                                             typename std::remove_pointer<U>::type>::value>::type>
      Wrapper(Wrapper<U> x) : val(x.getVal()) {}
    };
    
    void foo(Wrapper<const char *>) {}
    void foo(Wrapper<bool>) {}
    
    int main() {
      Wrapper<char *> cp;
      foo(cp);
    }
    

    使用此功能,您只能进行一定数量的转换,即:X * - &gt; const X *,整数类型之间的转换等。

    更新:不幸的是,似乎你无法模仿标准的重载决策规则,因为所有你可以使用的是转换运算符,并且在重载分辨率方面它具有恒定的等级