c++ - 返回类型自动扣除的好友函数模板无法访问私有(private)成员

标签 c++ templates language-lawyer c++14 friend

对不起,这个问题的标题太复杂了;我试图描述我为这个问题构建的最小 SSCCE。

我有以下代码:

#include <iostream>

namespace fizz
{
    template<typename... Ts>
    class bar
    {
    public:
        template<int I, typename... Us>
        friend auto foo(const bar<Us...> &);

    private:
        int i = 123;
    };

    template<int I, typename... Ts>
    auto foo(const bar<Ts...> & b)
    {
        return b.i;
    }
}

int main()
{
    std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}

此代码 compiles with GCC 5.2doesn't with Clang 3.7 :

main.cpp:19:18: error: 'i' is a private member of 'fizz::bar<int, float>'
        return b.i;
                 ^
main.cpp:25:24: note: in instantiation of function template specialization 'fizz::foo<1, int, float>' requested here
    std::cout << fizz::foo<1>(fizz::bar<int, float>{});
                       ^
main.cpp:13:13: note: declared private here
        int i = 123;
            ^

但是,如果您稍微更改代码(尽管以某种方式对我来说并不完全有用,因为在实际代码中这会引入大量样板文件):

#include <iostream>

namespace fizz
{
    template<typename... Ts>
    class bar
    {
    public:
        template<int I, typename... Us>
        friend int foo(const bar<Us...> &);

    private:
        int i = 123;
    };

    template<int I, typename... Ts>
    int foo(const bar<Ts...> & b)
    {
        return b.i;
    }
}

int main()
{
    std::cout << fizz::foo<1>(fizz::bar<int, float>{});
}

突然works with that Clang 3.7 .

不同的是,在不使用Clang编译的代码版本中,友元函数模板使用C++14的auto返回类型推导,而工作的则直接说返回intauto 返回类型推导的其他变体也会出现同样的问题,例如 auto &&const auto &

哪个编译器是正确的?请提供一些标准引用来支持答案,因为很有可能需要为一个(......希望不是两个)编译器提交一个错误......或者一个标准缺陷,如果两者都是正确的(这不会'不是第一次)。

最佳答案

我相信这是一个 clang 错误。我想从这个方向接近它。 auto 有什么皱纹占位符类型添加,与具有指定的返回类型相比?来自 [dcl.spec.auto]:

The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function declarator includes a trailing-return-type (8.3.5), that trailing-return-type specifies the declared return type of the function. Otherwise, the function declarator shall declare a function. If the declared return type of the function contains a placeholder type, the return type of the function is deduced from return statements in the body of the function, if any.

auto可以出现在foo的声明和定义,并且是有效的。

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. [ Example:

auto n = n;              // error, n’s type is unknown
auto f();
void g() { &f; }         // error, f’s return type is unknown
auto sum(int i) {
  if (i == 1)
    return i;            // sum’s return type is int
  else
    return sum(i-1)+i;   // OK, sum’s return type has been deduced
}

—end example ]

我们第一次需要确定表达式的类型时,函数的返回类型已经从 return 推导出来了。在 foo() 的定义中,所以这仍然有效。

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.

我们正在使用 auto在这两个地方,所以我们也没有违反这个规则。


简而言之,有几件事可以将特定返回类型与占位符返回类型与函数声明区分开来。但是 auto 的所有用法在示例中是正确的,所以命名空间范围 foo应该被看作是对第一个声明的 friend auto foo 的重新声明和定义。类模板内bar . clang 接受前者作为返回类型 int 的重新声明的事实但不适用于 auto ,对于 auto 没有相关的不同,绝对表明这是一个错误。

此外,如果您删除 int I模板参数,以便您可以调用foo不合格,clang 会将调用报告为模棱两可:

std::cout << foo(fizz::bar<int, float>{});

main.cpp:26:18: error: call to 'foo' is ambiguous
    std::cout << foo(fizz::bar<int, float>{});
                 ^~~
main.cpp:10:21: note: candidate function [with Us = <int, float>]
        friend auto foo(const bar<Us...> &);
                    ^
main.cpp:17:10: note: candidate function [with Ts = <int, float>]
    auto foo(const bar<Ts...>& b)
         ^

所以我们有 两个 名为 foo 的函数模板在相同的命名空间中(因为来自 [namespace.memdef] 的 friend 声明 foo 将把它放在最近的封闭命名空间中)接受相同的参数并具有相同的返回类型( auto )?那应该是不可能的。

关于c++ - 返回类型自动扣除的好友函数模板无法访问私有(private)成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32889492/

相关文章:

c++ - 使用 C++ WinAPI 在 Windows 10 上设置亮度

c++ - 使用Winsock的send()/recv()时,确认响应是否必要?

c++ - T 是否必须是完整类型才能在 `std::declval<T>` 中使用?

c++ - Constexpr 函数返回 union 成员 : g++ vs. clang++ : no diagnostics vs. 错误

c++ 显式多参数构造函数歧义

c++ - cv::Mat,在不知道矩阵类型的情况下在行列获取值

c++ - 在C++中的模板实例化中将带有构造函数的类用作类型参数

c++ - C++ 中的静态鸭子类型(duck typing)

c++ - 这个 "<>"在模板类函数中意味着什么?

c++ - 当返回类型是引用时有什么规则