I have this simple snippet of code see here:
template <class V> inline constexpr bool IsVoidTuple_t = std::is_same_v<V, std::tuple<void>>;
template<typename Type>
class C {
public:
using ResultOptionalType = std::conditional_t<IsVoidTuple_t<Type>, std::monostate, std::optional<Type>>;
};
int main() {
using ResultOptionalType = std::conditional_t<IsVoidTuple_t<std::tuple<void>>, std::monostate, std::optional<std::tuple<void>>>; // OK
using T = std::optional<std::tuple<void>>; // OK
C<std::tuple<void>> c1; // OK
auto c2 = std::make_unique<C<std::tuple<void>>>(); // OK
auto c3 = std::make_shared<C<std::tuple<void>>>(); // Error: forming reference to void
}
I'm not creating any explicit instance of std::tuple<void>
, also because the code itself comes from the need to prevent such case at compile time in my codebase. However, I am getting error forming reference to void
when creating the shared resource with make_shared<T>
and I cannot understand why this happens.
The thing that makes me really confused is that in all the other cases that I listed inside the main()
function I am able to compile without problems. The only difference that I might think it could cause the issue is the reference counting mechanism implemented by shared_ptr
.
TL;DR I would expect the above code to compile for all the cases listed in the main function body. Still, it seems that with shared pointers there is an hidden instantiation of a std::tuple<void>
.
I dived into the gcc's source code, and found it fell into a pattern like
foo(p); // p is of type C<std::tuple<void>>*
Here, the compiler will instantiate std::tuple<void>
due to ADL.
I'm unsure if this is a point that can be improved in the implementation of compilers. You can avoid it by using std::tuple<std::type_identity<void>>
rather than std::tuple<void>>
.