c++ 为什么MSVC在结构化绑定中发现不合格的get()?

ia2d9nvy  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(116)

请考虑以下代码:

#include <utility>
#include <type_traits>
#include <cstddef>
#include <iostream>

template <typename>
struct A
{
    void get() {}    // #1
};

template <typename ...Ts>
struct B : A<Ts>... {};

template <typename ...Ts>
struct std::tuple_size<B<Ts...>> : std::integral_constant<std::size_t, 2> {};

template <std::size_t I, typename ...Ts>
struct std::tuple_element<I, B<Ts...>>
{
    using type = int;
};

template <std::size_t I, typename ...Ts>
constexpr int get(B<Ts...>)    // #2
{
    return 2;
}

int main()
{
    B<double, long long> b;
    auto [x, y] = b;
    std::cout << x << ' ' << y << std::endl;
}

GCC接受上述代码并输出2, 2,因为我例外(godbolt),但MSVC抱怨(godbolt)。
错误C2385:'get'的不明确访问
根据cppreference
对于每个标识符,引入一个类型为“reference to std::tuple_element<i, E>::type”的变量:如果其对应的初始化器是左值,则为左值引用,否则为右值引用。
第i个变量的初始化式为

  • e.get<i>(),如果通过类成员访问查找在***E***的范围内获取的标识符找到至少一个声明,该声明是第一个模板参数为非类型参数的函数模板
  • 否则,get<i>(e),其中get仅通过参数相关查找进行查找,忽略非ADL查找。

根据我的理解,成员get()(第1行)不符合引用的要求(它甚至不是模板),编译器应该选择非成员get()(第2行)。GCC的工作方式和我预想的一样,但是MSVC似乎停留在成员get()上,忽略了它是否符合条件。
哪个编译器是正确的?为什么MSVC在结构化绑定中发现不合格的get()

xdnvmnnf

xdnvmnnf1#

[dcl.struct.bind]/4:
如果在E的作用域中搜索名称get,找到至少一个函数模板声明,其第一个模板参数是非类型参数,则初始化器为e.get<i>()
E是支持变量的类型,在本例中为B<double, long long>。)
如果搜索结果是不同基的成员之间的不确定性,我们会点击[class.member.lookup]/6,使程序格式不正确(不管找到的声明是否是函数模板)。
CWG2567是紧密相关的:它在查找重载运算符的上下文中处理类似的情况。
根据针对该问题提出的解决方案,此程序将变得格式良好:在B<double, long long>中搜索get将正常失败(不产生任何结果),允许使用非成员get作为回退。

相关问题