请考虑以下代码:
#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()
?
1条答案
按热度按时间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
作为回退。