3

考虑以下代码段:

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]

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

4

3 回答 3

2

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

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

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

于 2020-01-21T16:25:27.683 回答
2

编译器推断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);

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

于 2020-01-21T16:59:47.790 回答
1
clone<double>(42.0);

可能意味着

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

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

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

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

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

于 2020-01-21T16:24:24.817 回答