This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Help needed debugging std::is_convertible problem (PR 65760)
- From: Jonathan Wakely <jwakely dot gcc at gmail dot com>
- To: "libstdc++" <libstdc++ at gcc dot gnu dot org>
- Cc: Daniel KrÃgler <daniel dot kruegler at gmail dot com>
- Date: Thu, 16 Apr 2015 13:33:07 +0100
- Subject: Help needed debugging std::is_convertible problem (PR 65760)
- Authentication-results: sourceware.org; auth=none
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65760
I don't understand why commenting out *any one* of the lines marked
(1), (2), (3), (4) causes this to compile:
#include <functional>
struct C {
C() = default;
C(std::function<C(int)>); // (1)
C(std::function<C(int, int)>); // (2)
template <class T> C operator () (T&&); // (3)
template <class T> C operator () (T&&, T&&); // (4)
};
int main() {
C c = C();
}
My understanding is that the SFINAE constraint on the std::function
constructor needs to check std::is_convertible<C, C> which requires C
to be complete, but why does it only complain that C is incomplete
when all of (1) - (4) are present?
G++, Clang and EDG all agree on this behaviour, so I don't think it's
a compiler bug. The libc++ std::function works fine in this case, I
don't know what it does differently so that it works.
To fix it I am considering short-circuiting the constraint to not use
is_convertible<C, C>, by doing:
__or_<is_same<From, To>, is_convertible<From, To>>
which doesn't require the types to be complete when they're the same.
Before doing that I'd really like to understand the problem properly.
I've attached a reduced version with no header dependencies, based on
the relevant parts of <type_traits> and <functional> from 4.9.2.
namespace std
{
typedef decltype(sizeof(0)) size_t;
template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
typedef integral_constant<_Tp, __v> type;
};
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template<bool, typename, typename>
struct conditional;
template<typename...>
struct __or_;
template<>
struct __or_<>
: public false_type
{ };
template<typename _B1>
struct __or_<_B1>
: public _B1
{ };
template<typename _B1, typename _B2>
struct __or_<_B1, _B2>
: public conditional<_B1::value, _B1, _B2>::type
{ };
template<typename _B1, typename _B2, typename _B3, typename... _Bn>
struct __or_<_B1, _B2, _B3, _Bn...>
: public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
{ };
template<typename...>
struct __and_;
template<>
struct __and_<>
: public true_type
{ };
template<typename _B1>
struct __and_<_B1>
: public _B1
{ };
template<typename _B1, typename _B2>
struct __and_<_B1, _B2>
: public conditional<_B1::value, _B2, _B1>::type
{ };
template<typename _B1, typename _B2, typename _B3, typename... _Bn>
struct __and_<_B1, _B2, _B3, _Bn...>
: public conditional<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>::type
{ };
template<typename _Pp>
struct __not_
: public integral_constant<bool, !_Pp::value>
{ };
template<typename>
struct remove_cv;
template<typename>
struct __is_void_helper
: public false_type { };
template<>
struct __is_void_helper<void>
: public true_type { };
template<typename _Tp>
struct is_void
: public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
template<typename>
struct is_array
: public false_type { };
template<typename _Tp, std::size_t _Size>
struct is_array<_Tp[_Size]>
: public true_type { };
template<typename _Tp>
struct is_array<_Tp[]>
: public true_type { };
template<typename>
struct __is_pointer_helper
: public false_type { };
template<typename _Tp>
struct __is_pointer_helper<_Tp*>
: public true_type { };
template<typename>
struct is_lvalue_reference
: public false_type { };
template<typename _Tp>
struct is_lvalue_reference<_Tp&>
: public true_type { };
template<typename>
struct is_rvalue_reference
: public false_type { };
template<typename _Tp>
struct is_rvalue_reference<_Tp&&>
: public true_type { };
template<typename>
struct is_function;
template<typename>
struct is_function
: public false_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...)>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......)>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) volatile>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) volatile &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) volatile &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) volatile &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const volatile>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const volatile &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes...) const volatile &&>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const volatile>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const volatile &>
: public true_type { };
template<typename _Res, typename... _ArgTypes>
struct is_function<_Res(_ArgTypes......) const volatile &&>
: public true_type { };
template<typename _Tp>
struct is_reference
: public __or_<is_lvalue_reference<_Tp>,
is_rvalue_reference<_Tp>>::type
{ };
template<typename _Tp>
struct is_object
: public __not_<__or_<is_function<_Tp>, is_reference<_Tp>,
is_void<_Tp>>>::type
{ };
template<typename _Tp>
struct __is_referenceable
: public __or_<is_object<_Tp>, is_reference<_Tp>>::type
{ };
template<typename _Res, typename... _Args>
struct __is_referenceable<_Res(_Args...)>
: public true_type
{ };
template<typename _Res, typename... _Args>
struct __is_referenceable<_Res(_Args......)>
: public true_type
{ };
template<typename>
struct add_rvalue_reference;
template<typename _Tp>
typename add_rvalue_reference<_Tp>::type declval() noexcept;
template<typename, typename>
struct is_same;
template<typename, typename>
struct is_same
: public false_type { };
template<typename _Tp>
struct is_same<_Tp, _Tp>
: public true_type { };
template<typename _From, typename _To,
bool = __or_<is_void<_From>, is_function<_To>,
is_array<_To>>::value>
struct __is_convertible_helper
{ typedef typename is_void<_To>::type type; };
template<typename _From, typename _To>
class __is_convertible_helper<_From, _To, false>
{
template<typename _To1>
static void __test_aux(_To1);
template<typename _From1, typename _To1,
typename = decltype(__test_aux<_To1>(std::declval<_From1>()))>
static true_type
__test(int);
template<typename, typename>
static false_type
__test(...);
public:
typedef decltype(__test<_From, _To>(0)) type;
};
template<typename _From, typename _To>
struct is_convertible
: public __is_convertible_helper<_From, _To>::type
{ };
template<typename _Tp>
struct remove_const
{ typedef _Tp type; };
template<typename _Tp>
struct remove_const<_Tp const>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_volatile
{ typedef _Tp type; };
template<typename _Tp>
struct remove_volatile<_Tp volatile>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_cv
{
typedef typename
remove_const<typename remove_volatile<_Tp>::type>::type type;
};
template<typename _Tp, bool = __is_referenceable<_Tp>::value>
struct __add_rvalue_reference_helper
{ typedef _Tp type; };
template<typename _Tp>
struct __add_rvalue_reference_helper<_Tp, true>
{ typedef _Tp&& type; };
template<typename _Tp>
struct add_rvalue_reference
: public __add_rvalue_reference_helper<_Tp>
{ };
template<bool, typename _Tp = void>
struct enable_if
{ };
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };
template<bool _Cond, typename _Iftrue, typename _Iffalse>
struct conditional
{ typedef _Iftrue type; };
template<typename _Iftrue, typename _Iffalse>
struct conditional<false, _Iftrue, _Iffalse>
{ typedef _Iffalse type; };
template<typename _Tp>
struct __declval_protector
{
static const bool __stop = false;
static typename add_rvalue_reference<_Tp>::type __delegate();
};
template<typename _Tp>
inline typename add_rvalue_reference<_Tp>::type
declval() noexcept
{
static_assert(__declval_protector<_Tp>::__stop,
"declval() must not be used!");
return __declval_protector<_Tp>::__delegate();
}
template<typename _Functor>
inline _Functor&
__callable_functor(_Functor& __f)
{ return __f; }
template<typename _Signature>
class function;
template<typename _From, typename _To>
using __check_func_return_type
= __or_<is_void<_To>, is_convertible<_From, _To>>;
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
{
typedef _Res _Signature_type(_ArgTypes...);
template<typename _Functor>
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
(std::declval<_ArgTypes>()...) );
template<typename _Tp>
using _NotSelf = __not_<is_same<_Tp, function>>;
template<typename _Functor>
using _Callable
= __and_<_NotSelf<_Functor>,
__check_func_return_type<_Invoke<_Functor>, _Res>>;
template<typename _Cond, typename _Tp>
using _Requires = typename enable_if<_Cond::value, _Tp>::type;
public:
typedef _Res result_type;
function() noexcept { }
template<typename _Functor,
typename = _Requires<_Callable<_Functor>, void>>
function(_Functor);
};
}
struct C {
C() = default;
C(std::function<C(int)>);
C(std::function<C(int, int)>);
template <class T> C operator () (T&&);
template <class T> C operator () (T&&, T&&);
};
int main() {
C c = C();
}