3

I have the following code:

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

Everything compiles and works fine, however, if I #include<algorithm>, and compile with g++ -std=c++11, I get a very messy compile error (see below, related to std::uniform_int_distribution?!?!). It happens on the last line, when I explicitly specify the type double for the call to operator<<.

It works though on clang++ regardless of the -std=c++11 flag, and also on g++ without the -std=c++11 flag. What is happening here? I really have no idea, am I overloading somehow the global << defined in algorithm? Is this a g++ bug? (I use g++4.9 btw).

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,
       ^
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • `` somehow seems to include the file where libstdc++ has defined the function templates for output of distributions. Their implementation uses the first template argument as the template parameter of the distribution, therefore allowing implicit conversions for the form `operator<< (cout, 1.0)` (from the `double` to the distribution type). I'm not exactly sure why overloading doesn't fail due to ambiguity. – dyp Jul 20 '14 at 16:13
  • @dyp, exactly, the compiler should say "AMBIGUOUS CALL" – vsoftco Jul 20 '14 at 16:14
  • Probably the error happens earlier, during instantiation of the class template, which is required to look for ctors. – dyp Jul 20 '14 at 16:15
  • I think [this](http://coliru.stacked-crooked.com/a/54a7aceb1e941726) is happening. – dyp Jul 20 '14 at 16:17
  • `clang++` sometimes does not instantiate class templates if it doesn't need them for overload resolution. IIRC, this was covered [here](http://stackoverflow.com/q/22258054/420683) – dyp Jul 20 '14 at 16:21

1 Answers1

4

I think it boils down to this:

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

Note that in libstdc++'s implementation, the first template argument explicitly passed to operator<< is used as the template argument for the distribution (in the OP). So in theory, an implicit conversion from the second function argument of the operator<< call to the distribution type is possible.

Before checking for ambiguity, overloading resolution has to instantiate the class templates in order to check for converting constructors: If there are any, if they're explicit etc -- that is, if the conversion is viable.

Instantiating the class template for a non-integral type however is an error, checked by static_assert. So before one of the overload is selected, the compiler complains about the instantiation.

This instantiation is not strictly required if the compiler does not need it to select an overload, see [temp.inst]/7. All other overloads require a User-Defined Conversion (the parameter type is a class and different from the argument type), whereas the overload defined as friend function in the OP is an Exact Match. So checking for the viability of those other conversions is not required, those overloads won't be selected anyway.

Not instantiating the not required class templates is some kind of optimization - it is not mandatory. clang++ seems to perform it here, g++ doesn't (and instantiates the class templates). For another example and some discussion, see C++ inconsistency between gcc and clang

Community
  • 1
  • 1
dyp
  • 38,334
  • 13
  • 112
  • 177
  • 1
    ohh, I see, so the compiler makes sure that you are not trying to defined a `uniform_int_distribution` with a `double`... This makes sense, thanks for digging in. But, why does it compile then on `clang++` with `-std=c++11` flag? It doesn't check for the type? I'll try and see if I can use `double` for `uniform_int_distribution` with `clang` – vsoftco Jul 20 '14 at 16:21
  • You are right, indeed this is happening. Thanks much. – vsoftco Jul 20 '14 at 16:24