templatesc++17shared-ptrstdtuple

Making shared pointer of template class using std::tuple<void> tries to form a reference to `void`


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>.


Solution

  • 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>>.

    Demo