c ++模板和对重载函数

时间:2016-08-21 01:50:43

标签: c++ visual-studio templates overloading

我遇到了以下问题。我正在使用Xcode 7并且对我的项目没有任何问题。尝试在Visual Studio Express 2015上编译它后,我在代码中收到消息"Error C2668 ambiguous call to overloaded function"。我找不到任何与视觉工作室有关的问题。

我做了一个应该在VS上使用的最小工作示例(Xcode上没有错误)。奇怪的部分涉及func2部分。就好像VS编译器无法自动推导出更多类型和/或参数而不是限制。

更新:

Sam Varshavchik提出了一种解决方法,包括使用部分专业化和中间模板类。这是我想避免的解决方案。首先是因为它在上下文中不方便它将在我的代码中应用,第二,因为这个编译错误对我来说不清楚。此错误未显示在Xcode7中,即使在VS中,func2也没有错误。尽管我同意WhiZTiM的解释,但实际情况是,在这种情况下,重载有时会起作用,有时也不行。我真的很想知道原因。

更新2:

根据bogdan的说法,这可能是GCC和MSVC中的一个错误。我打算报告一下。 (第一周,我非常喜欢视觉工作室)

更新3:

https://connect.microsoft.com/VisualStudio/feedback/details/3076577

报告错误

functions.h:

template <class T>
class BX {
public :
    BX() {}
    ~BX() {}
};

template <class T1, class T2>
class G {
public :
    G() {}
    ~G() {}
};

template <template <class T> class T1, class T>
class DT {};

class B {
public :
    //I want func to work
    template <template <class T> class T1, class T, class M>
    static void func(const M& m, const DT<T1, T>* dt, T1<T>& val) {}

    template <template <class T> class T1, class T, class M>
    static void func(const G<T1<T>, M>& g, const DT<T1, T>* dt, T1<T>& val) {}

    //here is a small variation of func as a test
    template <template <class T> class T1, class T, class M>
    static void func2(const M& m, const DT<T1, T>* dt) {}

    template <template <class T> class T1, class T, class M>
    static void func2(const G<T1<T>, M>& g, const DT<T1, T>* dt) {}
};

的main.cpp

int main() {
    BX< int > bx;
    G<BX< int >, int> g;
    DT<BX, int>* dt;
    B::func(g, dt, bx);//Error  C2668   'B::func': ambiguous call to overloaded function
    B::func2(g, dt);//no error
}

3 个答案:

答案 0 :(得分:3)

你打了这个电话:

B::func(g, dt, bx);

其中:

  • g的类型为G<BX< int >, int>
  • dt的类型为DT<BX, int>*
  • bx的类型为BX< int >

现在你有这两个功能:

template <template <class T> class T1, class T, class M>
static void func(const M& m, const DT<T1, T>* dt, T1<T>& val) {}
               //^^^^^^^^^^

template <template <class T> class T1, class T, class M>
static void func(const G<T1<T>, M>& g, const DT<T1, T>* dt, T1<T>& val) {}
               //^^^^^^^^^^^^^^^^^^

在重载过程中;并且只考虑函数声明中的第一个参数(因为那些应该使函数声明不同):

    第一个函数重载中的
  • M被推导为G<BX< int >, int>
  • 第二个函数重载具有应匹配的模板化类型。
    • T可以从bx推断为int
    • T1<T>模板模板类型,从bx推断为BX< int >
    • M会匹配任何内容。
    • 在一天结束时,您推导出的第一个参数为G<BX< int >, int>,与第一个函数相同

GCC也会引发歧义错误。

要在传递G<...>类型时优先考虑第二个重载函数,您需要使用部分特化。 (因为它们的排名高于主要模板)。请参阅 Sam Varshavchik's answer有一种可行的方法。

答案 1 :(得分:2)

所示代码的明显意图是部分功能专业化。

哪个...不起作用。

那么,做什么,做什么......那么,如何将部分函数专业化转换为普通的模板类专业化呢?

我的解决方案专门针对第一个函数参数类型的模板,以消除静态类的歧义,并将其转发给两个终极类方法之一。

一个好的C ++编译器应该能够优化掉额外的函数调用层:

template <class T>
class BX {
public :
    BX() {}
    ~BX() {}
};

template <class Tdata, class Tmetric>
class G {
public :
    G() {}
    ~G() {}
};

template <template <class T> class T1, class T>
class DT {};

template<class M> class B_func;

class B {
public :
    template <template <class T> class T1, class T, class M>
    static void func(const M& m, const DT<T1, T>* dt, T1<T>& val)
    {
        B_func<M>::func(m, dt, val);
    }

    template <template <class T> class T1, class T, class M>
    static void func_a(const M& m, const DT<T1, T>* dt, T1<T>& val) {}

    template <template <class T> class T1, class T, class M>
    static void func_b(const G<T1<T>, M>& g, const DT<T1, T>* dt, T1<T>& val) {}

    //here is a small variation of func as a test
    template <template <class T> class T1, class T, class M>
    static void func2(const M& m, const DT<T1, T>* dt) {}

    template <template <class T> class T1, class T, class M>
    static void func2(const G<T1<T>, M>& g, const DT<T1, T>* dt) {}
};


template <class M>
class B_func {
public:
    template<class two, class three>
    static void func(const M& m, const two* dt, three& val)
    {
        B::func_a(m, dt, val);
    }
};

template <template <class T> class T1, class T, class M>
class B_func<G<T1<T>, M>> {
public:
    template<class two, class three>
    static void func(const G<T1<T>, M>& m, const two* dt, three& val)
    {
        B::func_b(m, dt, val);
    }
};


int main() {
    BX< int > bx;
    G<BX< int >, int> g;
    DT<BX, int>* dt;
    B::func(g, dt, bx);
    B::func2(g, dt);

    return 0;
}

答案 2 :(得分:1)

这看起来像MSVC和GCC中的错误。调用应该解决到第二次重载(Clang和EDG正在这样做)。

对于调用B::func(g, dt, bx),名称查找会找到两个func模板。对它们中的每一个执行模板参数推导和替换,以便生成随后可以参与重载解析的函数模板特化声明。两个模板的扣除成功,我们留下了两个专业:

void B::func<BX, int, G<BX<int>, int>>(const G<BX<int>, int>&, const DT<BX, int>*, BX<int>&);
void B::func<BX, int, int>            (const G<BX<int>, int>&, const DT<BX, int>*, BX<int>&);

这两个函数具有相同的参数声明子句,因此显然重载解析无法根据调用参数的转换来区分它们;它必须采用这个过程中的最后两个步骤。

首先,如果其中一个函数是模板特化而另一个函数不是,则优选非模板函数;这里不适用。

最后,它查看了合成两个特化声明的模板;如果根据函数模板的部分排序,其中一个模板比另一个模板更专业,则优选相应的特化。 (这是原始模板重新发挥作用的唯一过程。)

下面的描述不是很准确,并且跳过了一些细节,但我试图专注于与这种情况相关的部分。非常粗略:

  • 首先,从两个模板的函数参数声明中去除引用和cv限定符,产生:

    F1(M          , const DT<T1, T>*, T1<T>)
    F2(G<T2<U>, V>, const DT<T2, U>*, T2<U>)
    

    (更改模板参数名称以避免混淆)

  • 然后,尝试使用另一个模板的函数参数的形式作为参数来调用一个模板,然后反过来。在这种情况下,最后两对相应的参数具有相同的形式,因此推导成功。对于第一对相应的参数:

    • M形式的参数中推导出G<T2<U>, V>; M被推断为G<T2<U>, V>
    • T2形式的参数中导出U VG<T2<U>, V>M中的M并不起作用(G<T2<U>, V>可以是任何东西)。

    换句话说,M是一个更具体的&#34;形式比M;它不能代表F2可以代表的所有类型;这是更专业的在这种情况下试图形式化的直观含义。

  • 因此,演绎适用于从F1F2的所有相应参数对,但不是相反。这使得F1在部分排序方面比var fd = new FormData(); fd.append('name', name); fd.append('description', description); fd.append('image', image); $http.post(baseUrl, fd, { transformRequest: angular.identity, headers: {'Content-Type': undefined} }); 更专业。

这意味着在重载分辨率中首选对应于第二个模板的特化。