使用非类型模板和空 lambda 时,静态断言因无序映射而失败

static assertion failed with unordered map when using non-type template and empty lambda

以下 class 将在 std::map 内触发静态断言错误,因为分配器的 value_type 不同,尽管分配器是默认的:

template < bool store>
class MyClass {
  public:
   using hashmap_type = std::unordered_map< 
        int, 
        decltype([](){})
    >;

  private:
   std::map< int, hashmap_type > m_mapofmaps;
};

通过 Myclass<false> v{}; 触发器使用它

/opt/compiler-explorer/gcc-11.1.0/include/c++/11.1.0/bits/hashtable.h:191:21: error: static assertion failed: unordered container must have the same value_type as its allocator
  191 |       static_assert(is_same<typename _Alloc::value_type, _Value>{},

可以看出here (GCC 11.1):

但是如果删除非类型模板 store,编译器突然就没有问题了。这同样适用于将 lambda [](){} 与另一种类型交换,参见 here and here.

布尔非类型模板、lambda 和分配器之间的奇怪相互作用是什么?

似乎是 gcc 如何将默认参数替换为涉及未评估的 lambda 类型的模板参数的错误。

类似的例子:

#include <type_traits>

template<typename T, typename U = T>
struct assert_is_same {
    static_assert(std::is_same_v<T, U>);
};

template<bool B>
struct X {
    assert_is_same<decltype([](){})> x;
};

int main() {
    X<false> a;
}

如果没有 template<bool B>decltype([](){}) 不是依赖表达式,因此会更急切地求值(最终会得到类似 assert_is_same<_ZN1XILb0EEUlvE0_E, _ZN1XILb0EEUlvE0_E> 的结果)

在模板中,[](){} 的类型现在依赖于 B,因此无法立即替换为类型。似乎 gcc 将其扩展为 assert_is_same<decltype([](){}), decltype([](){})>,这两个 lambda 现在不同了。 (地图示例中的默认参数是 std::allocator<std::pair<const Key, T>>,或 std::allocator<std::pair<const int, decltype([](){})>>

一个解决方法是给 lambda 一个可以隐藏的名字:

private:
    static constexpr auto lambda = [](){};
public:
    using hashmap_type = std::unordered_map< 
        int, 
        decltype(lambda)
    >;
    // allocator is now `std::pair<const int, decltype(lambda)>`, and
    // the two `decltype(lambda)`'s have the same type