vendredi 1 mai 2015

std::make_pair type deduction

I came across some odd thing I would like to have an explanation for. The following code snippet provides a simple class template type and two operator<<s: one for specializations of type and one for a std::pair of type specializations.

#include <ostream>
#include <utility>

template <typename T>
class type {

public:

  T value_;

};

template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, type<T> const& a)
{
  return os << a.value_;
}

template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
{
  return os << a.first << ',' << a.second;
}

#include <iostream>

int
main()
{
  using float_type = type<float>;

  float_type const a = { 3.14159 };
  float_type const b = { 2.71828 };

#if 0
  std::cout << std::make_pair(a, b)
            << std::endl;
#else
  std::cout << std::pair<float_type const, float_type const>(a, b)
            << std::endl;
#endif
}

The main function provides a specialization and two variables of that specialization. There are two variants for displaying the variables as a std::pair. The first fails because std::make_pair seems to strip the const specifier from the variables, which in turn doesn't match with the signature of the second operator<<: std::pair<T const, T const>. However, constructing a std::pair specialization (second std::cout line in main) works as well as removing the const specification for T from operator<< for std::pair, i.e. std::pair<T, T>.

Compiler messages::

  • gcc 4.9.2

    std_make_pair.cpp: In function 'int main()':
    std_make_pair.cpp:52:35: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
       std::cout << std::make_pair(a, b) << std::endl;
                                   ^
    In file included from std_make_pair.cpp:3:0:
    /usr/include/c++/4.9.2/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::pair<type<float>, type<float> >]'
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
     ^
    
    
  • clang 3.5 (non-viable functions from system headers removed)

    std_make_pair.cpp:52:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>')
      and 'pair<typename __decay_and_strip<const type<float> &>::__type, typename __decay_and_strip<const
      type<float> &>::__type>')
      std::cout << std::make_pair(a, b) << std::endl;
      ~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~
    
    std_make_pair.cpp:30:1: note: candidate template ignored: can't deduce a type for 'T' which would make
      'const T' equal 'type<float>'
    operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
    
    

so, here's the question: am I supposed to specify an operator<< taking a std::pair of T instead of T const? Isn't that watering down the contract I'm setting up with any user of the functionality, i.e. with T const I basically promise to use T only in non-mutating ways?

Aucun commentaire:

Enregistrer un commentaire