mardi 30 janvier 2018

"partial specialization after instantiation" in boost headers

I have the following code:

foo.h

#include <boost/range/iterator_range.hpp>

#include <string>


inline void foo(const char* s)
{
    boost::iterator_range<std::string::const_iterator> r{
        s, s + std::char_traits<char>::length(s)
    };
    (void)r;
}

foo.cpp

#include <foo.h>
#include <boost/range/as_literal.hpp>
// some other code that uses as_literal

boost/range/as_literal.hpp is included by several headers under boost/algorithm, including but not limited to

#include <boost/algorithm/string/find.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/classification.hpp>

This compiles fine with clang 4.0 and trunk, but fails with GCC 4.8, 7.2 and trunk:

$ g++ -std=c++11 -Wall -pedantic -Wextra -Wformat=2 -Wshadow -Werror foo.cpp
/usr/include/boost/range/detail/str_types.hpp:26:12: error: partial specialization of ‘struct boost::range_const_iterator<T*>’ after instantiation of ‘struct boost::range_const_iterator<const char*, void>’ [-fpermissive]
     struct range_const_iterator<T*>
            ^~~~~~~~~~~~~~~~~~~~~~~~

Now I have a problem because foo.h cannot tell whether any of the "dangerous" boost headers are included after it. Users of foo.h will get a nasty surprise if they start using any of these headers (or worse, if they were already using those boost headers and add foo.h, their code won't compile - and they're going to complain to me that foo.h is broken).

The only solution I could find was to include boost/range/as_literal.hpp into foo.h, before the usage of boost::iterator_range, even though nothing in foo.h needs boost::as_literal.

This sounds like a bug in boost (1.62), because boost/range/as_literal.hpp includes boost/range/detail/str_types.hpp, which contains

namespace boost
{
    template< class T >
    struct range_const_iterator<T*>
    {
        typedef const T* type;
    };
}

whereas boost/range/iterator_range.hpp ultimately includes boost/range/const_iterator.hpp, which contains

namespace boost
{

template< typename C >
struct range_const_iterator_helper
        : extract_const_iterator<C>
{};

template<typename C, typename Enabler=void>
struct range_const_iterator
        : range_detail::range_const_iterator_helper<
            BOOST_DEDUCED_TYPENAME remove_reference<C>::type
        >
{
};

} // namespace boost

Defining the same struct with different content can't be right.

Which compiler is right? clang for accepting it or GCC for rejecting it?

Aucun commentaire:

Enregistrer un commentaire