template <typename T>
struct Wrapper
{
T val;
explicit Wrapper(T && v)
: val(std::move(v)) {}
explicit Wrapper(T const & v)
: val(v) {}
};
Wrapper<std::unique_ptr<int>> w { std::make_unique<int>(1) };
template struct Wrapper<std::unique_ptr<int>>;
template <typename T>
struct Wrapper
{
T val;
explicit Wrapper(T && v)
: val(std::move(v)) {}
explicit Wrapper(T const & v)
requires std::is_copy_constructible<T>::value
: val(v) {}
};
template struct Wrapper<std::unique_ptr<int>>;
I get a compile-time error because the attempt to instantiate the second constructor fails. So, I constrain the second constructor:
template <typename T>
struct Wrapper
{
T val;
explicit Wrapper(T && v)
: val(std::move(v)) {}
explicit Wrapper(T const & v)
requires std::is_copy_constructible<T>::value
: val(v) {}
};
My question is, with this constrain, should the explicit specialization of the class template succeed? Or, in other words, if a member function is 'eliminated' by the constraint, should it be instantiated during explicit class template instantiation? I know it is instantiated in GCC 6, but this feels wrong, doesn't it?
template struct Wrapper<std::unique_ptr<int>>;
I get a compile-time error because the attempt to instantiate the second constructor fails. So, I constrain the second constructor:
template <typename T>
struct Wrapper
{
T val;
explicit Wrapper(T && v)
: val(std::move(v)) {}
explicit Wrapper(T const & v)
requires std::is_copy_constructible<T>::value
: val(v) {}
};
My question is, with this constrain, should the explicit specialization of the class template succeed? Or, in other words, if a member function is 'eliminated' by the constraint, should it be instantiated during explicit class template instantiation? I know it is instantiated in GCC 6, but this feels wrong, doesn't it?In general, members are not "eliminated' by their constraints. They simply become uncallable -- especially in the implicit instantiation case.Explicit instantiation is worded a little differently. Effectively, you will not get an explicit instantiation of the copy constructor's declaration or definition since the constraints aren't satisfied. So, this should essentially eliminate the member.
An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, and provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation (14.10.2), except as described below.
To clarify Andrew's response: the behavior observed in the OP is a bug in GCC. P0121R0 modifies [temp.explicit]/8 as follows:An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, and provided that the associated constraints, if any, of that member are satisfied by the template arguments of the explicit instantiation (14.10.2), except as described below.The members whose constraints are not satisfied should not be explicitly instantiated.
template <class T>
struct A
{
template <class T1 = T, std::enable_if_t<std::is_integral_v<T1>>* = nullptr>
void foo()
{
T t;
t.foo();
}
};
template struct A<int>; // well-formed, no error
It works for C++ < 17 too, consider this: