Opened 9 years ago

Closed 8 years ago

Last modified 8 years ago

#9910 closed Bugs (fixed)

is_convertible doesn't compile with inaccessible copy constructor

Reported by: Matei David <matei@…> Owned by: John Maddock
Milestone: To Be Determined Component: type_traits
Version: Boost 1.55.0 Severity: Problem
Keywords: is_convertible Cc:

Description

I found a situation where (with gcc-4.8.2 & boost-1.55) I cannot compile a call to boost::is_convertible, but I can compile a similar call to std::is_convertible. This is a bug, unless it is undefined behaviour to pass is_convertible a type with a private or deleted copy constructor. If it is a bug, it is a boost bug, not a gcc bug (explained below).

Here is the discordant code:

#include <type_traits>
#include <boost/type_traits/is_convertible.hpp>
struct A {
    A() = default;
    A(const A&) = delete; // or private: A(const A&) = default;
};
struct Ref_A { // proxy reference to A
    operator A& () { return *_ptr; }
    A* _ptr;
};
int main() {
    (void)std::is_convertible< Ref_A, A >::value;
    (void)boost::is_convertible< Ref_A, A >::value;
}

I compile with: {g++|clang++} -std=c++11 -I${BOOST_INCLUDE} -Wall -Wextra -c

With gcc-4.8.2, the call to boost::is_convertible fails to compile, complaining that the copy constructor of A is deleted (or private.) The similar call to std::is_convertible compiles ok (and returns false, but that is irrelevant).

With clang-3.4, both calls compile. The reason for the discrepancy is that boost/type_traits/intrinsics.hpp defines the macro BOOST_IS_CONVERTIBLE(T,U) for clang, but not for gcc. Because of that, at the bottom of boost/type_traits/is_convertible.hpp, clang uses the macro to implement is_convertible, whereas gcc uses is_convertible_impl_dispatch instead (which is the one that fails). That's why I said this is not a gcc bug.

A temporary fix I found is to define that macro to defer to std:

#include <type_traits>
#include <boost/type_traits/intrinsics.hpp>
#ifndef BOOST_IS_CONVERTIBLE
#define BOOST_IS_CONVERTIBLE(T, U) (std::is_convertible<T, U>::value)
#endif
#include <boost/type_traits/is_convertible.hpp>

Which type_traits library should we prefer these days, post c++11? Intuition says std should be more stable/portable, is that wrong? If it is indeed std, is there a way (perhaps a macro?) to make boost mirror std (I'm talking about type_traits only). The reason for having such a mirror option is not for new code, but for other boost libraries which need type_traits. E.g, I came across this issue from boost::iterator.

Change History (4)

comment:1 by John Maddock, 8 years ago

Resolution: fixed
Status: newclosed

Fixed in develop by providing a C++11 conforming version.

I'm not so sure about std::is_convertible being more stable/conforming: there are quite a few bugs in the compiler intrinsics for type_traits that we have to work around: indeed MSVC's std::is_convertible fails this test case!

comment:2 by Michel Morin, 8 years ago

Hi John,

The use of is_abstract<To> in boost::detail::is_convertible_impl_dispatch_base disallows To to be an array of incomplete types. For example,

struct incomplete;
bool b = is_convertible<int, incomplete[]>::value;

produces a compilation error, but this code is fine in C++11.

Michel

comment:4 by Michel Morin, 8 years ago

Thanks for the fix!

Note: See TracTickets for help on using tickets.