The use case is for a subclass of std::expected, but the following reproduces the behaviour I'm interested in.
#include <type_traits>
template<class T>
struct Foo{
T value;
constexpr explicit operator bool(){return true;}
constexpr Foo() {}
template<class U=T>
constexpr explicit(!std::is_convertible_to<U,T>)
Foo(U&& v) : value(std::forward<U>(v)) {}
};
template <class T>
struct Bar : public Foo<T> {
using Foo<T>::Foo;
}
Bar inherits all of Foo's constructors, including their "explicitness"
But.
In gcc(12,13)
//Foo<bool> foo() {return Foo<int();} //does not compile, as expected
Bar<bool> bar(){ return Bar<int>();}
//compiles!
clang does not exhibit this behaviour; i.e. Bar behaves the same as Foo, and trying to return Bar<int> from a function returning Bar<bool> does not compile. godbolt
Adding the constructor--err, explicitly-- to Bar fixes the issue, but doing so for all the many complex constructors in std::expected would be...infeasible.
My question is:
Can someone with knowledge of the standard shed light on whether this is even a bug in gcc, or merely a loophole? At the moment I wouldn't know how to word the bug report.
Finally, can someone help me find a workaround (short of copy-pasting most of <expected> into my little subclass)? I'm working with gcc-12,(std=c++23,linux,x64) on this project, clang doesn't support expected, or other features in c++23.
what i've tried:
template<class T>
using Bar = Foo<T>;
works, but without the ability to customize Foo, which is the point
template <class T>
struct Bar : public Foo<T> {
Bar()=default;
template<class...Args>
Bar(Args&&...args){
Foo<T>& self = *this;
self = { std::forward<Args>(args)...};
}
};
gets close, but shifts requirements from constructors to operator=, plus requires a default constructors on T, and doesn't work for constructors with multiple arguments. (std::expected has a few that take tags: std::in_place_t, std::unexpect_t)
I can maybe work around the above, but it's getting farther from a transparent wrapper.
this question deals with this subject but predates conditional-explicit (c++20)
this question Deals with the Intel compiler, and mentions section 12.9 in some version of the standard, which I read as saying that all characteristics of inherited constructors should be the same, which is reassuring.
Other questions with similar keywords don't handle this intersection of conditional-explicit plus inherited constructors
Edit:
i've found these bugs on the gcc bug tracker which precisely match my situation.
So that answers my first question: yes its a bug, it's on the gcc bug tracker, and the relevant bit of the standard is [namespace.udecl] p13
Constructors that are named by a using-declaration are treated as though they were constructors of the derived class when looking up the constructors of the derived class ([class.qual]) or forming a set of overload candidates ([over.match.ctor], [over.match.copy], [over.match.list]).
Still leaving this open in the hopes that some workaround can be thought of.
It appears to be a bug.
when I rewrite
Baras the above, the inherited constructors inFoohave the proper kind ofexplicit. And if I add astatic_assert( this is not explicit )to theBarconstructor, it is triggered by theBar<int>toBar<bool>implicit conversion, yet the conversion occurs implicitly.we can start here to fix your problem. This, as noted, causes you to lose implicit/explicit flags. We repeat the trick I used above.
We start off with
is_explicitly_constructible, which isis_constructible. Now we try to writeis_implicitly_constructible:this invokes copy-list initialization in a concept-friendly context.
we can now write our
Bar:and I think that does most of what you want.
The big thing you'll be missing is
{}based construction ofBar's (well,Foo<T>'s) construction arguments.