简体   繁体   English

检测 map 实现是否支持不完整类型

[英]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++.我想看看 map 实现是否支持不完整的类型,因为 libc++ 似乎支持它们,但 stdlibc++ 不支持。

I tried SFINAE here, but it gives a compilation error:我在这里尝试了 SFINAE,但它给出了一个编译错误:

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++.我想看看 map 实现是否支持不完整的类型,因为 libc++ 似乎支持它们,但 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.首先,因为 class 模板的成员函数是延迟实例化的,所以对不完整类型的“支持”不是二元的是或否。 Perhaps one can instantiate the class, but not use it.也许可以实例化 class,但不能使用它。 Perhaps one can default construct the class, but not copy it.也许可以默认构造 class,但不能复制它。 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.然而,评论表明实际的愿望是定义一个以 map 作为成员的类型,这只需要实例化。 To test this, for example, replace M{} with typename M::value_type where M is the map type.例如,要对此进行测试,请将M{}替换为类型名称typename M::value_type ,其中M是 map 类型。

Second, to test this with SFINAE, one stills need the class template author's support.其次,要用SFINAE测试这个,一张剧照需要class模板作者的支持。 SFINAE only tests failures in the immediate context. SFINAE 仅测试直接上下文中的故障。 Any other errors are still hard errors.任何其他错误仍然是硬错误。 These are the type of errors from libstdc++.这些是来自 libstdc++ 的错误类型。 This also explains the is_constructible observation in @sklott's answer.这也解释了@sklott 回答中的is_constructible观察。 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). class 模板的 Static 数据成员可以在翻译单元的末尾(但在私有模块片段之前)实例化。 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 .如果有一个假设的is_complete_v<T> ,任何用T incomplete 实例化它的翻译单元都不能完成T Any translation unit that completes T must not include the declaration that depends on is_complete_v<T> .任何完成T的翻译单元不得包含依赖于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.第四,标准 C++ 表示未定义以不完整类型实例化std::mapstd::unordered_map 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++.第二点、第三点和第四点的结果是,您只能在同意让您作为标准 C++ 的扩展的实现上执行此操作。

libc++ supports instantiation as an extension. libc++ 支持实例化作为扩展。 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.要检查 map 是否支持不完整类型,您需要确定 map 本身是否为完整类型。 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.完整类型和不完整类型之间的唯一区别是无法获取不完整类型的大小,即无法获取不完整类型的sizeof() 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.顺便说一句,GCC 11.3 似乎有错误,因为即使是std::is_constructible<std::unordered_map<int, incomplete_type>>::value也会给出同样的错误,这绝对是不正确的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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