【问题标题】:explicit template parameter specification for ostream& operator<< puzzling compile errorostream& 运算符的显式模板参数规范<< 令人费解的编译错误
【发布时间】:2014-07-20 16:03:17
【问题描述】:

我有以下代码:

#include <iostream>
//#include <algorithm> // compile error when `g++ -std=c++11`, fine otherwise

using namespace std;

template<typename T>
class Foo
{
    T x_;
public:
    Foo(const T& x={}): x_(x){}; // default ctor, bounded rvalue default-initialized

    template<typename S>
    friend ostream& operator<<(ostream& lhs, const Foo<S>& rhs)
    {
        return lhs << rhs.x_;
    }
};

int main()
{
    Foo<double> foo(10.1);
    cout << foo << endl;
    operator<<(cout, foo) << endl;

    //puzzling compile-time error when #include<algorithm> and compile 
    // with `g++ -std=c++11`, otherwise fine (both clang++ and g++)
    operator<< <double>(cout, foo) << endl; 
}

一切都可以编译并且工作正常,但是,如果我 #include&lt;algorithm&gt; 并使用 g++ -std=c++11 编译,我会收到一个非常混乱的编译错误(见下文,与 std::uniform_int_distribution 相关?!?!)。它发生在最后一行,当我为调用operator&lt;&lt; 明确指定类型double 时。

不管-std=c++11 标志如何,它都可以在clang++ 上工作,也可以在没有-std=c++11 标志的g++ 上工作。这里发生了什么?我真的不知道,我是否以某种方式重载了algorithm 中定义的全局&lt;&lt;? 这是g++ 错误吗? (我使用 g++4.9 顺便说一句)。

In file included from /opt/local/include/gcc49/c++/random:49:0,
                 from /opt/local/include/gcc49/c++/bits/stl_algo.h:66,
                 from /opt/local/include/gcc49/c++/algorithm:62,
                 from /Users/vlad/minimal.cpp:2:
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::uniform_int_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::uniform_int_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:1668:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::geometric_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::geometric_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:4010:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::negative_binomial_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::negative_binomial_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:4210:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::poisson_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::poisson_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:4432:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::binomial_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::binomial_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:3779:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^
/opt/local/include/gcc49/c++/bits/random.h: In instantiation of 'class std::discrete_distribution<double>':
/Users/vlad/minimal.cpp:25:34:   recursively required by substitution of 'template<class _IntType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::discrete_distribution<_IntType>&) [with _IntType = double; _CharT = char; _Traits = std::char_traits<char>]'
/Users/vlad/minimal.cpp:25:34:   required from here
/opt/local/include/gcc49/c++/bits/random.h:5253:7: error: static assertion failed: template argument not an integral type
       static_assert(std::is_integral<_IntType>::value,
       ^

【问题讨论】:

  • &lt;algorithm&gt; 似乎包含了 libstdc++ 定义分布输出的函数模板的文件。他们的实现使用第一个模板参数作为分布的模板参数,因此允许对operator&lt;&lt; &lt;double&gt;(cout, 1.0) 形式的隐式转换(从double 到分布类型)。我不确定为什么重载不会因为歧义而失败。
  • @dyp,确切地说,编译器应该说“AMBIGUOUS CALL”
  • 错误可能更早发生,在类模板的实例化过程中,这是查找 ctor 所必需的。
  • 我认为this 正在发生。
  • clang++ 有时不会实例化类模板,如果它不需要它们来解决重载问题。 IIRC,这被覆盖了here

标签: c++ c++11 g++


【解决方案1】:

我认为归结为:

#include <type_traits>

template<class T>
struct foo
{
    static_assert(std::is_integral<T>{}, "T must be integral");
};

template<class T> struct bar {};

template<class T, class Stream>
Stream& operator<<(Stream& p, foo<T> const&);

template<class T, class Stream>
Stream& operator<<(Stream& p, bar<T> const&);

int main()
{
    int x;
    operator<< <double>(x, 1.0);
}

请注意,在 libstdc++ 的实现中,显式传递给 operator&lt;&lt; 的第一个模板参数用作分发的模板参数(在 OP 中)。所以理论上,从operator&lt;&lt; 调用的第二个函数参数到分布类型的隐式转换是可能的。

在检查歧义之前,重载解析必须实例化类模板以检查转换构造函数:如果有,它们是否是显式的等等——也就是说,如果转换是可行的 em>。

然而,为非整数类型实例化类模板是一个错误,由static_assert 检查。因此,在选择其中一个重载之前,编译器会抱怨实例化。

如果编译器不需要它来选择重载,则不严格要求此实例化,请参阅 [temp.inst]/7。所有其他重载需要用户定义的转换(参数类型是类,不同于参数类型),而在 OP 中定义为友元函数的重载是精确匹配。因此,不需要检查其他转换的可行性,无论如何都不会选择这些重载。

不实例化不需要的类模板是某种优化——它不是强制性的。 clang++ 似乎在这里执行它,g++ 没有(并实例化类模板)。另一个例子和一些讨论,见C++ inconsistency between gcc and clang

【讨论】:

  • 哦,我明白了,所以编译器确保您没有尝试用 double 定义 uniform_int_distribution... 这是有道理的,感谢您的深入研究。但是,为什么然后用-std=c++11 标志在clang++ 上编译?它不检查类型?我会尝试看看是否可以将double 用于uniform_int_distributionclang
  • 你是对的,这确实正在发生。非常感谢。
猜你喜欢
  • 2021-02-17
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多