【问题标题】:C++ using a template argument to resolve an overloadC++ 使用模板参数来解决重载
【发布时间】:2017-08-15 19:56:12
【问题描述】:

我正在编写一个包装器模板类,它可以包装任意类型并为其注入一些额外的语义,但我不知道如何让重载解决方案正常工作。当通常通过比较竞争转换序列的等级来解决的转换无法由编译器推断时,就会出现问题,因为所讨论的类型是模板参数,而不是函数参数。例如,

#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&lt;const char *&gt;),就像cp 代替char *foo 代替void foo(const char *) 一样。这可能吗?

编辑:感谢大家的快速回复,但也许我应该更清楚。我上面给出的只是一个例子。我需要的是对以下问题的一般解决方案:给定任意类型 TUV,假设 C++ 的内置重载决议更喜欢转换 T -> U 而不是 @987654332 @ -> V。那么我如何才能确保 C++ 更喜欢 Wrapper&lt;T&gt; -> Wrapper&lt;U&gt; 而不是 Wrapper&lt;T&gt; -> Wrapper&lt;V&gt;

我做出这个澄清是因为答案似乎专门针对重载解决方案的某些方面,例如 cv 合格性,而我确实需要一个通用的解决方案。

【问题讨论】:

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


    【解决方案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】:

    你需要让构造函数不那么贪婪。这可以通过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&lt;bool&gt;) 重载的情况下不起作用,您必须将 SFINAE 的应用程序从 Wrapper 的构造函数直接移动到此重载:

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

    【讨论】:

    • 感谢您的回复,但它似乎只解决了我给出的示例,可能还有其他一些类似的案例。请参阅我最近的编辑。
    【解决方案3】:

    你在char*前面缺少const

    如下声明。它应该工作。

    Wrapper<const char *> cp;
    

    下面是测试和结果

    http://rextester.com/FNOEL65280

    【讨论】:

    • OP 想要一种方法来进行转换。这只是回避了问题
    【解决方案4】:

    您可以做的事情很少:

    1. 只禁止从 Wrapper 构造 Wrapper。这样的事情很容易出错
    2. 使用 SFINAE
    #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 *-> const X *,整数类型之间的转换,等等。

    更新:不幸的是,您似乎无法模仿标准的重载解析规则,因为您只能使用转换运算符,并且在重载解析方面它具有恒定等级

    【讨论】:

    • 哎呀。如果您的 UPDATE 是真的,那么我会非常失望。我不是那么秘密地希望其他人会插话不同意!
    • 我偷偷分享这个希望
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    • 2015-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 2021-03-19
    相关资源
    最近更新 更多