指针类型的特殊重载

Special overload for pointer types

考虑以下片段:

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 是指针类型时才应该通过。

clone<double>(42.0);

可能意味着

  • "instantiate the first clone with the explicit template argument double",或

  • "instantiate the second clone with the first argument explicitly given as double and the second one deduced from 42.0 being passed in place of const U& v"。您提供的默认值将被忽略,U 是从匹配 doubleconst U& 推导出来的。

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

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

但是这个函数的语义还是很奇怪的。有时函数 returns 是堆分配的指针,有时它 returns 是一个值。用户必须传递目标类型(在两种情况下还是只传递一种?)。这在调用站点会非常混乱。

Why is the compiler deriving T=double, U=double in line 22? I thought it only should pass if T is a pointer type.

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

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

编译器推断 U=double 并且根本没有使用您的默认参数。我认为您可以尝试根据 .

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);

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