【问题标题】:Output operator for class template with variadic template pack带有可变参数模板包的类模板的输出运算符
【发布时间】:2014-04-29 03:49:30
【问题描述】:

我尝试编写一个模板类和输出操作符,如下所示:

#include <iostream>

namespace N
{

template< typename ...types >
struct X
{
    static_assert((sizeof...(types) != 0), "zero length");
    X() = default;
    X(X const &) = default;
    X(X &&) = default;
    template< typename ...args >
    //explicit // does not matter
    X(args &&...) { ; }
    int value = 10;
};

template< typename ...types >
std::ostream &
operator << (std::ostream & out, X< types... > const & x)
{
    return out << x.value;
}

} // namespace N

int main()
{
    using namespace N;
    X< float > /*const*/ x; // `const` does not matter
    std::cout << x << std::endl;
    return 0;
}

但是static_assertion 提出了:

main.cpp:9:5: error: static_assert failed "zero length"
    static_assert((sizeof...(types) != 0), "zero length");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:32:23: note: in instantiation of template class 'N::X<>' requested here
    std::cout << x << std::endl;
                      ^
1 error generated.

如果类模板X 和operator &lt;&lt; 重载在全局namespace 中定义,那么一切都是一样的。我发现,评论 using namespace N; 行并将 X&lt; float &gt; 替换为 N::X&lt; float &gt; 可以解决问题。

如何解释这种行为?是什么原因?

编辑:

我找到了解决办法:就是把operator &lt;&lt;重载的模板参数改成如下:

template< typename first, typename ...rest >
std::ostream &
operator << (std::ostream & out, X< first, rest... > const & x)
{
    return out << x.value;
}

拆分类的typename ..types 是不必要的。此外,由于代码极度臃肿,这根本不可取。

【问题讨论】:

  • 使用 '\n' 而不是多余的 std::endl 也可以修复它。

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


【解决方案1】:

重现问题的简单方法:

int main()
{
    using namespace N;
    std::cout << std::endl;
}

在这种情况下,候选函数是来自namespace std 的operator&lt;&lt; 的所有重载,来自std::ostream 的所有成员运算符namespace N 的函数模板operator&lt;&lt;。

13.3.1/7:“候选函数模板是函数模板,候选函数模板特化是使用模板参数推导生成的”

因此,在重载解析开始之前,必须从 std::endl 推导出 X&lt;types...&gt; const&amp;,这是一个模板函数的地址。函数的地址是函数指针类型,将N::X&lt;types...&gt; const&amp;与指针类型匹配的唯一方法是将types...推导出为空列表。

(替换当然会失败,因为没有从任何函数指针类型到N::X&lt;&gt;的隐式转换,这会悄悄地消除重载,因为它是不可行的,但是静态断言不在即时上下文中并且是一个硬错误)

故事的寓意:使用指令是邪恶的。

【讨论】:

  • 很好的解释。但是将types... 推断为空列表还不够清楚。
  • 如何将N::X&lt; types... &gt; const &amp; 视为decltype(std::endl) 类型的指针?
  • 根据这个答案,将template&lt; typename ...types &gt; struct X 替换为template&lt; typename first, typename ...rest &gt; struct X 应该可以解决问题。
  • @Dukales 它可以从一个转换。如果没有其他人停下来提供更多详细信息,我明天会回到这里
  • 遗憾的是,编译器拒绝该程序是正确的,因为在重载解决过程中未指定模板特化是否被实例化,即使在那些不必要的情况下也是如此(第 14.7.1/7 节)。可以通过将N::X 保留为不完整的demonstrate 来说明实例化是不必要的。在我看来,OP 是语言缺陷的受害者。
猜你喜欢
  • 1970-01-01
  • 2014-09-08
  • 1970-01-01
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多