依赖于参数的查找是否还包括在不可访问的基础中设置的重载?

问题描述 投票:0回答:0

这是一个简单的exaple

template<typename T>
struct t1 : protected T
{
};

template<typename T>
struct t2
{
    template<auto Index>
    struct inner : t1<T>
    {
    private:
        template<auto I>
            requires(Index == I)
        [[nodiscard]] friend constexpr const T& get(const inner& t) noexcept
        {
            return t;
        }
    };
};

template<typename T, typename U>
struct t3 : t2<T>::template inner<0>, t2<U>::template inner<1>
{
};

void foo()
{
    const t3<false_type, tuple<int, float>> v{};
    auto v1 = get<0>(v);
    auto v2 = get<1>(v);
}

类型

inner
有一个隐藏的
friend get
,但是
std::tuple
在全局函数中也有
std::get
,它会发出以下错误。

Clang 输出:

:34:15: 错误:对“get”的调用不明确

auto v1 = get<0>(v);

/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/tuple:1795: 5:注意:候选函数[with __i = 0, _Elements = ] get(const tuple<_Elements...>& __t) noexcept

:19:49: 注:候选函数 [I = 0]

[[nodiscard]] friend constexpr const T& get(const inner& t) noexcept

GCC 给出了类似的错误:

: 在函数 'void foo()' 中:

:34:21: 错误:调用重载的 'get<0>(const t3, std::tuple >&)' 是不明确的

auto v1 = get<0>(v);

包含在 :1:

的文件中

/opt/compiler-explorer/gcc-trunk-20230312/include/c++/13.0.1/tuple:1795:5: 注意:候选人:

'constexpr std::__tuple_element_t<__i, std::tuple<_UTypes ...> >& std::get(const tuple<_UTypes ...>&) [with long unsigned int __i = 0; _Elements = {int, float}; __tuple_element_t<__i, tuple<_UTypes ...> > = int]'

get(const tuple<_Elements...>& __t) noexcept

:19:49: 注意:候选人:'constexpr const T& get(const t2::inner&) [with auto I = 0;自动索引 = 0; T = std::integral_constant]'

[[nodiscard]] friend constexpr const T& get(const inner& t) noexcept

令人惊讶的是,MSVC 接受了代码。

如果我们用

struct t1 : protected T
替换
struct t1 : private T
,MSVC会打印:

error C2243: 'type cast': 从 'const t2::inner<0> *' 到 'const T &' 的转换存在,但无法访问

cppreference.com 中的adl 描述说:

...

否则,对于函数调用表达式中的每个参数,都会检查其类型以确定将添加到查找中的关联的命名空间和类集。

  1. 对于基本类型的参数,关联的命名空间和类集为空

  2. 对于类类型的参数(包括union),集合由

    组成

a) 班级本身

b) 它的所有直接和间接基类

c) 如果该类是另一个类的成员,则它是该类的成员

d) 添加到集合中的类的最内层封闭命名空间

...

总之,编译器为

std::get
实例化
std::tuple
使用
inner
作为参数而不进行访问检查,对吗?

c++ inheritance access-control argument-dependent-lookup
© www.soinside.com 2019 - 2024. All rights reserved.