如何消除重载模板函数的歧义?

How to disambiguate overloaded template functions?

以下代码有问题。虽然第 1 部分没问题,但问题出在 main() 的第 2 部分。编译时,会显示一条不明确的错误消息。如何更改代码以解决歧义?

template<typename Arg> void func(Arg arg) 
{  
    arg();
}
template<typename Arg, typename... Args> void func(Arg arg, Args... args) 
{  
    func(args...);
    arg();
}
template<typename Container> void func(Container & c) 
{
    for (typename Container::reverse_iterator i = c.rbegin(); i != c.rend(); ++i ) 
    { 
        (*i)();
    } 
}

void f()
{
    std::cout << "+" ;
}

int main()
{
    //1
    func(f,f,f);

    //2    
    std::vector<std::function<void()> > v{f,f};
    func(v);
}

Link 编码:http://cpp.sh/3wxrc

How can I change the code to resolve the ambiguity?

也许使用模板模板?

template <template <typename ...> class Cont, typename ... Ts>
void func (Cont<Ts...> & c) 
{
    for (typename Cont<Ts...>::reverse_iterator i = c.rbegin(); i != c.rend(); ++i ) 
    { 
        (*i)();
    } 
}

删除基于 func() Container 的版本,很明显。

简单地定义一个模板参数Container,不要让它与通用Arg模板参数不同。

我知道您在函数内部使用了 typename Cont<Ts...>::reverse_iterator。但是编译器必须根据函数签名而不是函数体来选择正确的重载。

使用 Cont<Ts...> 参数,你有更专业的东西。

问题是它无法推断 func 调用第一个还是第三个。这就是您可以在不更改函数重载层次结构的情况下使其工作的方法,或者按照上一条评论中的回答进行操作

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
template<typename Arg> 
void func(Arg* arg) 
{  
    arg();
}
template<typename Arg, typename... Args> 
void func(Arg arg, Args... args) 
{  
    func(args...);
    arg();
}
template<typename Container> 
void func(Container&& c) 
{
    for (typename Container::reverse_iterator i = c.rbegin(); i != c.rend(); ++i ) 
    { 
        (*i)();
    } 
    //OR Which is bettere to use
    /*for(auto && e : c){
        e();
    }*/
}

void f()
{
    std::cout << "+" ;
}

int main()
{
    //1
    func(f,f,f);

    //2    
    std::vector<std::function<void()> > v{f,f};
    func(std::move(v));
}

如果你有 C++17,那么 std::enable_if_t 可以与 std::is_invocable_v 一起使用(这需要 C++17):

template<typename Arg>
std::enable_if_t<std::is_invocable_v<Arg>>
func(Arg arg) 
{  
    arg();
}

https://wandbox.org/permlink/E2PoQdMv1pwXdMgO

我会首先从单个作业中拆分(可变)迭代:

// func overloads with one parameter.

template <typename ...Ts>
void funcs(Ts&&... args) 
{
    const int dummy[] = {(func(std::forward<Ts>(args)), 0)..., 0};
    static_cast<void>(dummy); // Avoid warning for unused variable

    // Or in C++17:
    // (func(std::forward<Ts>(args)), ...);
}

那么对于只有一个参数的方法,任何左值引用都是不明确的,因为签名是:

template<typename Arg> void func(Arg arg);
template<typename Container> void func(Container & c);

您可以使用 SFINAE 来区分它们:

template<typename Arg>
auto func(Arg arg)
-> decltype(arg(), void())
{  
    arg();
}

template<typename Container>
auto func(Container& c)
-> decltype(c.rbegin() != c.rend(), (*c.rbegin())(), void())
{
    for (auto it = c.rbegin(); it != c.rend(); ++it) 
    { 
        (*it)();
    } 
}

Demo