【问题标题】:ambiguous template pattern matching when using variadic template arguments of function overloads使用函数过载的Variadic模板参数时模糊模板模式匹配
【发布时间】:2013-08-27 05:19:45
【问题描述】:

这应该是可变参数模板的常见情况,例如当作为可变参数模板参数的树行走子级时。我发现了许多相关的问题和答案,但它们要么是关于稍微不同的事情,要么是相同的事情,我没有得到它。 现在来解决问题。我有一个像这样的非可变元组

template <class E, class T1, class T2, class T3, etc...>
struct X;

并且我正在重载函数以具有专门的行为,具体取决于此类元组的第一个元素是指针类型还是向量指针类型。 这很好用,但是如果我将模板参数打包到一个可变参数模板参数中,那么重载就会变得模棱两可。这是错误消息:

variadic.cpp:42:17: error: ambiguous overload for ‘operator<<’ in ‘std::cout << y’
variadic.cpp:42:17: note: candidates are:...

当编译器尝试将vector&lt;double*&gt;* 作为元组的第一个元素进行匹配时,它应该更喜欢X&lt;vector&lt;V*&gt;*,T*...&gt; 而不是X&lt;H*,T*...&gt;

我可以使用 enable_if 来消除歧义,然后一切正常。但是我想了解这个错误,如果可能的话找到其他方法然后 enable_if。代码如下:

#include <iostream>
#include <vector>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/utility/enable_if.hpp>

using namespace std;

template <typename ... T>
struct X;

template <>
struct X <>
{   
};  

template <typename H, typename ... T>
struct X<H*,T*...> : public X<T*...>
{
        H* value;
        X(H* value, T*... args)
                : value(value), X<T*...>(args...)
        {
        }
};

template <typename H, typename ... T>
#ifdef DO_NOT_WANNA_SEE_THE_BUG
typename boost::enable_if<boost::is_fundamental<H>, std::ostream>::type&  
       operator<<(std::ostream& stream, X<H*,T*...> const & x)
#else
std::ostream& operator<<(std::ostream& stream, X<H*,T*...> const & x)
#endif
{
        return stream << "specialized scalar pointer";
}

template <typename V, typename ... T>
std::ostream& operator<<(std::ostream& stream, X<vector<V*>*,T*...>  const & x)
{
        return stream << "specialized vector pointer";
}

int main()
{
        double a,b;
        vector<double *> v;
        X<double*,double*> x (&a,&b);
        X<vector<double*>*, double*> y (&v, &b);
        cout << x << endl;
        cout << y << endl; // this line is ambiguous according to gcc 4.6 and later
}

【问题讨论】:

  • 这根本没有帮助,但 Apple LLVM 4.2 (clang) 可以毫无问题地解决这个问题。我很想知道 gcc 是否在没有可变参数 T*... 的情况下也有同样的问题(据我所知,这是不需要的),而是你使用了 T...。它为什么重要是没有意义的,但话又说回来,gcc 正在呕吐,而 clang 并非如此......see it live on ideone.com
  • 跟进:我刚刚注意到 ideone.com 使用 gcc 4.8,仅供参考。
  • @WhozCraig。完美的答案。你说的对。我想将所有内容都限制为指针,但对于 Head 来说当然就足够了。打包的 Tail 不需要它。您的建议也适用于旧的 gcc 4.6。您对 LLVM 的评论也很有趣。非常感谢!

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


【解决方案1】:

精华来自 WhozCraig 的评论

当您在两个模板中删除 T* 时,程序将编译(带有警告 -Wreorder)并给出预期的输出。

operator<<(std::ostream& stream, X<H*,T...> const & x)
operator<<(std::ostream& stream, X<vector<V*>*, T...>  const & x)

在没有任何启发的情况下浏览了[14]个模板后,我认为存在编译器错误。

修改后的测试:

#include <iostream>
#include <vector>
#include <boost/type_traits/is_fundamental.hpp>
#include <boost/utility/enable_if.hpp>

using namespace std;

#define USE_VARIADIC_TEMPLATE 1
#define USE_AMBIGUOUS 1

template <typename ... T>
struct X;

template <>
struct X <>
{
};

template <typename H, typename ... T>
struct X<H*,T*...> : public X<T*...>
{
    H* value;
    X(H* value, T*... args)
    :  X<T*...>(args...), value(value)
    {}
};

#if USE_VARIADIC_TEMPLATE
template <typename H, typename ... T>
#if USE_AMBIGUOUS
std::ostream& operator<<(std::ostream& stream, X<H*,T*...> const & x)
#else
std::ostream& operator<<(std::ostream& stream, X<H*,T...> const & x)
#endif
#else
template <typename H, typename T>
std::ostream& operator<<(std::ostream& stream, X<H*, T*> const & x)
#endif
{
        return stream << "specialized scalar pointer";
}

#if USE_VARIADIC_TEMPLATE
template <typename V, typename ... T>
#if USE_AMBIGUOUS
std::ostream& operator<<(std::ostream& stream, X<vector<V*>*,T*...>  const & x)
#else
std::ostream& operator<<(std::ostream& stream, X<vector<V*>*,T...>  const & x)
#endif
#else
template <typename V, typename T>
std::ostream& operator<<(std::ostream& stream, X<vector<V*>*, T*>  const & x)
#endif
{
        return stream << "specialized vector pointer";
}

int main()
{
        double a,b;
        vector<double *> v;
        X<double*,double*> x (&a,&b);
        X<vector<double*>*, double*> y (&v, &b);
        cout << x << endl;
        cout << y << endl; // this line is ambiguous according to gcc 4.6 and later
}

即使是这样:

template <typename H, typename T>
std::ostream& operator<<(std::ostream& stream, X<H*, T*> const & x)
template <typename V, typename T>
std::ostream& operator<<(std::ostream& stream, X<vector<V*>*, T*>  const & x)

编译得很好。

编辑: 如果可变参数模板只包含一个作为 T* 传递的 T,它应该是上面显示的简单情况。

【讨论】:

  • 我更喜欢 WhozCraig 的修改,因为要保持所有 T*... 解包应该被删除,而不仅仅是函数重载模板的那些。在修改后的代码中,您还应该删除最后一条注释,因为它在该上下文中令人困惑。
  • ...我现在同意这里有一个编译器错误但不是一个严重的错误,因为无论如何它应该是一个警告。正确的答案是你和之前 WhozCraig 指出的那样,我写了一些愚蠢的东西,感谢 SO,我现在知道了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 1970-01-01
相关资源
最近更新 更多