【问题标题】:How to disambiguate overloaded template functions?如何消除重载模板函数的歧义?
【发布时间】:2019-08-27 13:16:45
【问题描述】:

以下代码有问题。虽然 1 部分没问题,但问题在于 main() 的第二部分。在编译时,会显示一条模棱两可的错误消息。如何更改代码以解决歧义?

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

代码链接:http://cpp.sh/3wxrc

【问题讨论】:

  • 看起来你需要一些 SFINAE。定义类型特征is_container,然后在您的1) 声明中添加enable_if&lt;!is_container&lt;Arg&gt;&gt;
  • 我想它会起作用,但看起来有点麻烦......

标签: c++ c++11 templates overloading variadic-templates


【解决方案1】:

如何更改代码以解决歧义?

也许使用模板模板?

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&lt;Ts...&gt;::reverse_iterator。但是编译器必须根据函数签名选择正确的重载,而不是根据函数体。

使用Cont&lt;Ts...&gt; 参数,您可以获得更专业的内容。

【讨论】:

  • 您仍然没有检查typename Cont&lt;Ts...&gt;::reverse_iterator i = c.rbegin(); 是否有效。这可以防止将func 与模板仿函数一起用作输入。如果在任何时候有必要这样做,我们会按照 Martin Morterol 的建议回到 SFINAE。
  • @generic_opto_guy - 你是对的,但在我看来问题(和 OP 准备)更基本。我不知道是否是引入 SFINAE 的情况,其中重点是更少或更多的专业化。
【解决方案2】:

如果你有 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

【讨论】:

    【解决方案3】:

    我会首先从单一工作中拆分(可变参数)迭代:

    // 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

    【讨论】:

    • 很好的解决方案!在#2 行,你可能也想打电话给funcs()
    • @Qwertypal:演示已修复。
    【解决方案4】:

    问题在于它无法推断出哪个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));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-07-26
      • 1970-01-01
      • 2013-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多