简体   繁体   English

具有自动返回类型扣除的朋友功能模板无法访问私有成员

[英]Friend function template with automatic return type deduction cannot access a private member

Sorry for how complicated the title of this question is; 很抱歉这个问题的标题有多复杂; I tried to describe the minimal SSCCE I constructed for this problem. 我试图描述我为这个问题构建的最小SSCCE。

I have the following code: 我有以下代码:

#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>{});
}

This code compiles with GCC 5.2 and doesn't with Clang 3.7 : 此代码使用GCC 5.2进行编译,不是使用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;
            ^

However, if you change the code slightly (although in a way that is not exactly useful for me, since in the real code this would introduce tons of boilerplate): 但是,如果你稍微更改代码(虽然对我来说不是很有用,因为在实际代码中这会引入大量的样板):

#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>{});
}

it suddenly works with that Clang 3.7 . 它突然与Clang 3.7一起使用

The difference is that in the version of the code that doesn't compile with Clang, the friend function template uses C++14 auto return type deduction, while the working one plainly says it returns int . 不同之处在于,在不使用Clang编译的代码版本中,友元函数模板使用C ++ 14 auto返回类型推导,而工作者明确表示它返回int The same problem also happens with other variants of auto return type deduction, like auto && or const auto & . auto返回类型推导的其他变体也会出现同样的问题,例如auto &&const auto &

Which compiler is right? 哪个编译器是对的? Please provide some standard quotes to support the answer, since it is quite possible that a bug will need to be filed for one (...hopefully not both) compilers... or a standard defect, if both are right (which wouldn't be the first time). 请提供一些标准报价以支持答案,因为很可能需要为一个(...希望不是两个)编译器提出错误...或标准缺陷,如果两者都是正确的(这不会'这是第一次)。

I believe it's a clang bug. 我相信这是一个铿锵的错误。 I want to approach it from this direction. 我想从这个方向接近它。 What wrinkles does the auto placeholder type add, as compared to having a specified return type? 与具有指定的返回类型相比, auto占位符类型添加了什么皱纹? From [dcl.spec.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. 在这样的声明有效的任何上下文中,占位符类型可以在decl-specifier-seqtype-specifier-seqconversion-function-idtrailing-return-type中与函数声明符一起出现。 If the function declarator includes a trailing-return-type (8.3.5), that trailing-return-type specifies the declared return type of the function. 如果函数声明包含trailing-return-type (8.3.5),则trailing-return-type指定函数的声明返回类型。 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. 如果函数的声明返回类型包含占位符类型,则函数的返回类型将从函数体中的return语句推导出来(如果有)。

auto can appear in foo 's declaration and definition, and is valid. 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. 但是,一旦在函数中看到了return语句,从该语句推导出的返回类型就可以在函数的其余部分中使用,包括在其他return语句中。 [ 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 ] - 末端的例子]

The first time we need to use determine the type of an expression, the return type of the function will already have been deduced from the return in the definition of foo() , so this is still valid. 我们第一次需要使用确定表达式的类型时,函数的返回类型已经从foo()定义中的return推断出来,所以这仍然有效。

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. 具有使用占位符类型的声明返回类型的函数或函数模板的重新声明或特化也应使用该占位符,而不是推导类型。

We're using auto in both places, so we don't violate this rule either. 我们在这两个地方都使用auto ,所以我们也没有违反这条规则。


In short, there are several things that differentiate a specific return type from an placeholder return type from a function declaration. 简而言之,有几种方法可以将特定的返回类型与占位符返回类型与函数声明区分开来。 But all the usages of auto in the example are correct, so the namespace-scope foo should be seen as a redeclaration and definition of the first-declared friend auto foo within class template bar . 但是示例中auto所有用法都是正确的,因此命名空间范围foo应该被视为类模板bar第一个声明的friend auto foo的重新声明和定义。 The fact that clang accepts the former as a redeclaration for return type int but not for auto , and there is no relevant different for auto , definitely suggests this is a bug. 那铿锵接受前者为返回类型的重新声明的事实int但不适用于auto ,而且是没有相关的不同的auto ,无疑表明,这是一个错误。

Further, if you drop the int I template parameter so that you can call foo unqualified, clang will report the call as ambiguous: 此外,如果删除int I模板参数以便可以调用foo unqualified,则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)
         ^

So we have two function templates named foo in the same namespace (since from [namespace.memdef] the friend declaration for foo will place it in the nearest enclosing namespace) that take the same arguments and have the same return type ( auto )? 所以我们在同一个命名空间中有两个名为foo函数模板(因为从[namespace.memdef] foofriend声明将它放在最近的封闭命名空间中),它们采用相同的参数并具有相同的返回类型( auto )? That shouldn't be possible. 这应该是不可能的。

It appears that your first example should work. 看来您的第一个示例应该可行。 There is a statement in C++14 (7.1.6.4 p12): C ++ 14(7.1.6.4 p12)中有一个声明:

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. 具有使用占位符类型的声明返回类型的函数或函数模板的重新声明或特化也应使用该占位符,而不是推导类型。 [ Example: [例如:

. . .

template <typename T> struct A {
    friend T frf(T);
};
auto frf(int i) { return i; } // not a friend of A<int>

The reason for the example appears to be to explain that to make the declarations match (and cause the defined function to be a friend) the declaration of frf inside struct A would also need to use auto . 这个例子的原因似乎是解释为了使声明匹配(并使定义的函数成为朋友),struct A中frf的声明也需要使用auto This implies to me that having a friend declaration with an auto return type and later defining the friend function (and also using auto) is allowed. 这对我来说意味着允许使用具有自动返回类型的朋友声明并稍后定义友元函数(并且还使用自动)。 I can't find anything that would make this work differently for a member function template, like in your example. 我找不到任何会使成员函数模板的工作方式不同的内容,例如在您的示例中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM