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

方圆圆*_*方圆圆 6 c++ language-lawyer c++17 structured-bindings

考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

GCC 接受上述代码并2, 2按照我的例外进行输出 ( godbolt ),但 MSVC 抱怨 ( godbolt )。

错误 C2385:“get”的访问不明确

根据cppreference

对于每个标识符,引入一个类型为“引用 std::tuple_element<i, E>::type”的变量:如果其对应的初始值设定项是左值,则为左值引用,否则为右值引用。

第 i 个变量的初始值设定项是

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

根据我的理解,成员get()(第 #1 行)不满足引用的要求(它甚至不是模板),编译器应该选择非成员get()(第 #2 行)。GCC 按照我的方式工作,但 MSVC 似乎停留在 member 上get(),忽略了它是否符合资格。

哪个编译器是正确的?为什么 MSVC 认为不符合get()结构化绑定条件?

duc*_*uck 1

[dcl.struct.bind]/4

get如果在 范围内搜索名称时E发现至少一个声明是函数模板,且其第一个模板参数是非类型参数,则初始值设定项为e.get<i>()

E在本例中是支持变量的类型B<double, long long>。)

如果搜索结果在不同基数的成员之间存在歧义,我们会点击[class.member.lookup]/6,从而使程序格式错误(无论找到的声明是否属于函数模板)。


CWG2567密切相关:它在查找重载运算符的上下文中处理类似的场景。

get根据该问题的拟议解决方案,该程序的结构良好:对in的搜索B<double, long long>将优雅地失败(不会产生任何结果),从而允许将非成员get用作后备。