对不起,这个问题的标题太复杂了;我试图描述我为这个问题构建的最小 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.2和 doesn'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>{});
}
不同的是,在不使用Clang编译的代码版本中,友元函数模板使用C++14的auto
返回类型推导,而工作的则直接说返回int
。 auto
返回类型推导的其他变体也会出现同样的问题,例如 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/