【问题标题】:Special overload for pointer types指针类型的特殊重载
【发布时间】:2020-01-21 16:16:48
【问题描述】:

考虑以下 sn-p:

template <class T>
struct remove_pointer
{
};

template <class T>
struct remove_pointer<T*>
{
    typedef T type;
};

template <typename T>
T
clone(const T& v)
{
    return v;
}


template <typename T, typename U = typename remove_pointer<T>::type>
T
clone(const U& v)
{
    return new U(v);
}


int main() 
{
    auto foo = clone<double>(42.0);
    return 0;
}

此代码会产生编译错误:

 In function 'int main()':
30:34: error: call of overloaded 'clone(double)' is ambiguous
30:34: note: candidates are:
14:1: note: T clone(const T&) [with T = double]
22:1: note: T clone(const U&) [with T = double; U = double]

为什么编译器在第 22 行派生 T=double, U=double?我认为只有当T 是指针类型时它才应该通过。

【问题讨论】:

  • 默认模板仅在未提供或推导时使用。在这里推导出来。
  • 明白!如果 T 是指针,我可以让专门的重载通过吗?
  • 您希望能够拥有clone&lt;double*&gt;(42.0)。但是double* p = /*..*/; clone&lt;double*&gt;(p) 呢?
  • 你想达到什么目的?如果clone 得到一个T* 它应该返回一个T ?
  • 当给定int** 时,clone 应该做什么?

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


【解决方案1】:

编译器推导出U=double 并且根本没有使用您的默认参数。我认为您可以尝试基于this answer 为您的clone 函数设置两种不同的重载。

您的示例可能如下所示:

#include <iostream>
#include <type_traits>

template <typename T>
T
clone(const T& v, typename std::enable_if<!std::is_pointer<T>::value >::type* = 0)
{
    std::cout << "non ptr clone" << std::endl;
    return v;
}

template <typename T>
T
clone(const T v, typename std::enable_if<std::is_pointer<T>::value >::type* = 0)
{
    std::cout << "ptr clone" << std::endl;
    using noptrT = typename std::remove_reference<decltype(*v)>::type;
    return new noptrT(*v);
}

int main()
{
    auto foo = clone<double>(42.0);
    std::cout << foo << std::endl;
    double* ptr = new double(69.69);
    auto bar = clone<double*>(ptr);
    std::cout << *bar << std::endl;
    delete ptr;
    delete bar;
    return 0;
}

请注意,此解决方案还允许您传递函数指针

void fun() {}
...
clone<void(*)()>(fun);

这将导致编译错误(新不能应用于函数类型)。

【讨论】:

    【解决方案2】:

    为什么编译器在第 22 行推导出 T=double, U=double?我认为只有当 T 是指针类型时它才应该通过。

    因为模板参数是根据传递的非模板参数推导出来的。您将双重文字传递给const U&amp; 类型的参数,因此U 被推断为double。如果推导参数,则不使用模板参数的默认值。

    问题是单个模板参数以及两个模板参数重载都可以使用相同的参数列表调用,因此编译器不知道您打算调用哪个重载。

    【讨论】:

      【解决方案3】:
      clone<double>(42.0);
      

      可能意味着

      • “使用显式模板参数double 实例化第一个clone”,或

      • “实例化第二个clone,第一个参数明确给出为double,第二个从42.0推导出来代替const U&amp; v”。您提供的默认值被忽略,U 是从匹配 doubleconst U&amp; 推导出来的。

      编译器无法确定您指的是哪一个,因此您会收到错误消息。也许您打算使用 SFINAE 来专门化功能模板。但强烈建议不要专门使用函数模板 (http://www.gotw.ca/publications/mill17.htm)。

      表达你想要的一种方式是if constexprhttps://godbolt.org/z/_YGXGc

      但是这个函数的语义还是很奇怪的。有时该函数返回一个堆分配的指针,有时它返回一个值。用户必须传递目标类型(在两种情况下还是只有一种?)。这在呼叫现场会很混乱。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-11
        • 2019-12-17
        • 1970-01-01
        • 2014-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-24
        相关资源
        最近更新 更多