简体   繁体   中英

Detect if map implementation supports incomplete types

I want to see if a map implementation supports incomplete types, as libc++ seems to support them, but not stdlibc++.

I tried SFINAE here, but it gives a compilation error:

template<template<typename, typename, typename...> typename MapType, typename = void>
struct allows_incomplete : std::false_type {};

struct incomplete_type;
template<template<typename, typename, typename...> typename MapType>
struct allows_incomplete<MapType, std::void_t<decltype(MapType<std::string_view, incomplete_type>{})>> : std::true_type {};

static_assert(allows_incomplete<std::map>::value);
static_assert(!allows_incomplete<std::unordered_map>::value);

I want to see if a map implementation supports incomplete types, as libc++ seems to support them, but not stdlibc++.

This is basically impossible.

First, because member functions of class templates are lazily instantiated, "support" for incomplete types is not a binary yes or no. Perhaps one can instantiate the class, but not use it. Perhaps one can default construct the class, but not copy it. At least some member functions will typically have completeness as a precondition.

The question tests default constructibility. However, the comments suggest the actual desire is to define a type with the map as a member, which only requires instantiation. To test this, for example, replace M{} with typename M::value_type where M is the map type.

Second, to test this with SFINAE, one stills need the class template author's support. SFINAE only tests failures in the immediate context. Any other errors are still hard errors. These are the type of errors from libstdc++. This also explains the is_constructible observation in @sklott's answer. It is not a bug.

Third, testing completeness for reasons other than to fail compilation is dangerous. Static data members of class templates can be instantiated at the end of the translation unit (but before the private module fragment). They must give the same answer wherever they are instantiated. If one had a hypothetical is_complete_v<T> , any translation unit that instantiates it with T incomplete must not complete T . Any translation unit that completes T must not include the declaration that depends on is_complete_v<T> . While possible, this would be very fragile.

Fourth, standard C++ says it is undefined to instantiate std::map and std::unordered_map with an incomplete type. Compilation failure is actually the nice option here versus walking into a trap.

The consequence of the second, third, and fourth points is that you can only do this on implementations that agree to let you as an extension to standard C++.

libc++ supports instantiation as an extension. See here and here . I cannot find documentation of the extension but it is in the test suite.

So the only real solution is this:

#include <version>
#ifdef _LIBCPP_VERSION

To check if map supports incomplete type you need to determine if such map is complete type itself or not. The only difference between complete and incomplete type is that it is not possible to get size of incomplete type, ie its impossible to get sizeof() of incomplete type. So, we can construct detector for incomplete types based on this. Something like this:

#include <type_traits>
#include <map>
#include <unordered_map>
#include <iostream>

namespace detail {
    template<int N>
    struct test {using type = void;};
}

template<typename T, typename = void>
struct is_complete : std::false_type {};

template<typename T>
struct is_complete<T, typename detail::test<sizeof(T)>::type> : std::true_type {};

struct incomplete_type;
struct complete_type {};

template<template<typename, typename, typename...> typename MapType>
using allows_incomplete = is_complete<MapType<int, incomplete_type>>;

int main()
{
    std::cout << std::boolalpha << is_complete<incomplete_type>::value << std::endl;
    std::cout << std::boolalpha << is_complete<complete_type>::value << std::endl;
    std::cout << std::boolalpha << is_complete<std::map<int, incomplete_type>>::value << std::endl;
    std::cout << std::boolalpha << is_complete<std::unordered_map<int, incomplete_type>>::value << std::endl;
    std::cout << std::boolalpha << allows_incomplete<std::map>::value << std::endl;
    std::cout << std::boolalpha << allows_incomplete<std::unordered_map>::value << std::endl;
}

BTW, GCC 11.3 seems to have bug, because even std::is_constructible<std::unordered_map<int, incomplete_type>>::value gives same error and this is defenitelly incorrect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM