为什么我可以在 MSVC 和 icc 中为 glm::vec 创建用户定义的结构化绑定,但不能在 Clang 和 GCC 中创建?

Why can I create user defined structured bindings for glm::vec in MSVC and icc, but not Clang and GCC?

我在过去一年左右的时间里一直在 MSVC 项目中使用类似于以下的代码,并且 运行 遇到了一个问题,试图将它与 g++ 一起使用。

#include <utility>
#include <glm/glm.hpp>

namespace std {
  template< std::size_t I, auto N, class T, auto Q>
  constexpr auto& get(glm::vec<N, T, Q>& v) noexcept { return v[I]; }

  template< std::size_t I, auto N, class T, auto Q>
  constexpr const auto& get(const glm::vec<N, T, Q>& v) noexcept { return v[I]; }

  template< std::size_t I, auto N, class T, auto Q>
  constexpr auto&& get(glm::vec<N, T, Q>&& v) noexcept { return std::move(v[I]); }

  template< std::size_t I, auto N, class T, auto Q>
  constexpr const auto&& get(const glm::vec<N, T, Q>&& v) noexcept { return std::move(v[I]); }

  template <auto N, class T, auto Q>
  struct tuple_size<glm::vec<N, T, Q>> : std::integral_constant<std::size_t, N> { };

  template <std::size_t I, auto N, class T, auto Q>
  struct tuple_element<I, glm::vec<N, T, Q>> {
    using type = decltype(get<I>(declval<glm::vec<N,T,Q>>()));
  };
}// end std


auto f(){
  auto [x,y,z] = glm::vec3(1);
  return x + y + z;
}

GCC 给出错误 error: 'get' was not declared in this scope; did you mean 'std::get'?
Clang 给出错误 error: use of undeclared identifier 'get'
icc 和 MSVC 都可以正确编译。

我想知道这是否与 GLM 的实现有关,因为我过去从未遇到过与 GCC 的自定义结构化绑定问题。

我想知道是否有人知道这里发生了什么。是 icc 和 MSVC 因接受代码而表现不正确,还是 Clang 和 GCC 因拒绝代码而表现不正确?

以下是在 Compiler Explorer 上处理此问题的四种不同编译器的示例: https://godbolt.org/z/6PCWyn

我相信 GCC 和 Clang 是正确的。

以下是 cppreference 关于结构化绑定的内容:

The expression std::tuple_size<E>::value must be a well-formed integer constant expression, ...

For each identifier, a variable whose type is "reference to std::tuple_element<i, E>::type" is introduced ... The initializer for the i-th variable is

  • e.get<i>(), if lookup for the identifier get in the scope of E by class member access lookup finds at least one declaration that is a function template whose first template parameter is a non-type parameter
  • Otherwise, get<i>(e), where get is looked up by argument-dependent lookup only, ignoring non-ADL lookup.

(强调我的)

您必须将 get() 重载移动到 namespace glm