为什么我可以在 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
。
我在过去一年左右的时间里一直在 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 identifierget
in the scope ofE
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)
, whereget
is looked up by argument-dependent lookup only, ignoring non-ADL lookup.
(强调我的)
您必须将 get()
重载移动到 namespace glm
。