C++ 如何在编译时选择正确的重载函数模板?

C++ 如何在编译时选择正确的重载函数模板?,c++,gcc,overloading,sfinae,function-templates,C++,Gcc,Overloading,Sfinae,Function Templates,我试图理解如何在编译时选择正确的重载函数模板,但编译器给了我一个困难的时间。我能做到,但我不明白发生了什么。让我解释一下 我有两个结构A和B,如下所示。一个具有特殊功能,另一个具有正常功能 struct A { void special() { std::cout << "special called.\n"; } }; struct B { void normal() { std::cout << "normal called.\n"; } }; 我

我试图理解如何在编译时选择正确的重载函数模板,但编译器给了我一个困难的时间。我能做到,但我不明白发生了什么。让我解释一下

我有两个结构A和B,如下所示。一个具有特殊功能,另一个具有正常功能

struct A
{
    void special() { std::cout << "special called.\n"; }
};

struct B
{
    void normal() { std::cout << "normal called.\n"; }
};
我的意图是要有一种机制,它在编译时根据特殊函数是否可用来选择正确的重载函数模板。我运行了两个函数,它们将结构作为参数,因此可以调用适当的函数

template<class Func, Func f> struct Sfinae {};

template <typename U>
static void run(U& u, Sfinae<void (U::*)(), &U::special>* = 0)
{
    u.special();
}

template <typename U>
static void run(U& u, ...)
{
    u.normal();
}
我已经用以下方法对此进行了测试,得到了各种结果:

int main()
{
    A a;
    run<A>(a, 0); // works
    run<A>(a); // ERROR: ambiguous overloaded function
    run(a, 0); // ERROR: A has no member normal
    run(a); // ERROR: ambiguous overloaded function

    B b;
    run<B>(b, 0); // works
    run<B>(b); // works
    run(b, 0); // works
    run(b); // works

    return 0;
}
我想把它作为runa使用,不需要任何额外的参数或参数。当这不起作用时,我的代码是否有问题

另外,我很想知道这里发生了什么,为什么这是这样的推论,所以我需要为A而不是为B而付出?我不知道标准是怎么说的,也不知道编译器之间是否有差异,但至少Linux上的gcc4.4.4和Mac上的gcc4.0.1的工作方式与我描述的一样


有人能解释一下吗?谢谢

对于这种特殊情况,您可以这样做,这非常简单:

template <typename U>
static void run(U & u)
{
    u.special();
}

template <>
static void run<B>(B &u)
{
    u.normal();
}
或者,您可以简单地删除模板,然后编写两个重载函数。我同意,这并不能从更一般的角度解决问题

也许,本主题将帮助您找到一个通用解决方案:


见约翰的回答:-

这在这里会有用的。它有点假设这两个函数正常和特殊是互斥的,也就是说,一个类有一个函数却没有另一个函数。我相信你能使它适应你的目的。当然,这使用boost::enable_if

#include <iostream>
#include <boost/utility/enable_if.hpp>

struct A
{
    void special() { std::cout << "special called.\n"; }
};

struct B
{
    void normal() { std::cout << "normal called.\n"; }
};

template<int> struct Sfinae { enum { value = true }; };

template <typename U>
static typename boost::enable_if<Sfinae<sizeof(&U::special)>,void>::type run(U& u)
{
    u.special();
}

template <typename U>
static typename boost::enable_if<Sfinae<sizeof(&U::normal)>,void>::type run(U& u)
{
    u.normal();
}


int main()
{
    A a;
    run(a); // works

    B b;
    run(b); // works

    return 0;
}

这适用于Linux上的gcc 4.6.0。

为什么要在正常版本的run中使用可变参数?@Zac:可变参数最不适合重载解析。我们的想法是,只有在没有其他匹配项的情况下,才应该选择这个重载。我明白了,但是对于他所展示的内容,没有必要选择它。@Zac,你能扩展你的评论吗?我所展示的只是我试图简化的东西,以展示所涉及的核心部分,以及编译器正在接受/拒绝的内容。我很想得到一个解释,为什么会是这样。根据你所描述的,纳瓦兹给出的答案就是你所需要的。也就是说,您似乎没有对函数中的可变参数执行任何操作,因此不需要它们。只需将模板专门化为需要专门化的类型,其余的则保持通用。谢谢。我知道这一点,但是,如果有很多A和B,并且我希望代码在情况发生变化时也能工作,那么这可能是不切实际的。假设我添加或删除B或A。也谢谢你的链接。虽然我确实想解决我试图实现的目标,但我也有兴趣理解我所描述的特定事情。为什么会这样?谢谢你,米克尔。这解决了问题,但我也有兴趣了解我所看到的代码。例如,我想。。。是推断时最后匹配的。在这里,如果有一个默认参数,并且没有参数传递给函数,则情况似乎不是这样。我不明白这一点,也不知道这是否是预期的行为标准。有什么想法吗?