C++-转换函数模板推断,为什么这个有效?

8
class A
{
    struct B{};
public:
    static void test(A::B){}
};

struct C
{
    template<class T>
    operator T()
    {
        return T();
    }
};

int main()
{
    A::test(C());
}

这段代码适用于clang 3.7、gcc 5.1和vc++ 14.2。
存在两个问题:
1. 为什么模板可以推断类型为A::B?(太聪明了!)
据我所知,模板是通过返回语句而不是参数来推断类型的。
但我在N4606 12.3.2 6中发现了一些有趣的东西A conversion function template shall not have a deduced return type (7.1.7.4)。 (然而,我无法找到更多关于此的信息,因为7.1.7.4太难理解了。)
2. 为什么转换函数模板可以访问A::B?

谢谢。

2个回答

4
为什么模板可以推导出类型为A::B?(好聪明!)
因为test()接收的是A::B,所以你需要一种方法将C转换为A::B。我们可以尝试通过转换函数进行初始化,在[over.match.conv]中有相关规定:
"S及其基类的转换函数都会被考虑。那些不在S中隐藏且返回类型为T或者可通过标准转换序列(13.3.3.1.1)转换为T的非显式转换函数均为候选函数。"
我们按照[temp.conv]的规定执行模板推导:
"通过比较转换函数模板P的返回类型(记作P)与所需的转换结果类型(记作A;请参见8.6、13.3.1.5和13.3.1.6以确定该类型)进行模板参数推导,如14.8.2.5所述。"
基本上,我们推导出了template operator T()中的T为A::B。这是一个良好的转换序列,也是唯一合适的转换序列,所以就这样发生了。
你引用的关于“推断返回类型”的那行代码,指的是返回类型中的auto或decltype。但这里并没有这种情况。
为什么转换函数模板可以访问A::B?
访问规则严格限制于名称。只有名称B是private的,但我们并没有访问名称,而是直接推导类型。
B的构造函数是public的,因此转换函数的主体也是良好的,所以代码的所有部分都是良好的。

我在哪里可以获取有关使用“type”而不是“name”进行访问控制的更多信息? - Caesar
@Caesar,类型并不存在访问控制这一说法。 - Barry
但是当编译器尝试构建 B 时,编译器不会违反访问控制吗? - Caesar
@Caesar 不是的,就像我在答案中所说,访问控制是关于名称的。我们没有访问到名称 B,但我们不是通过名称访问它。 - Barry
我还是不明白。:| - Caesar
显示剩余2条评论

0
略微简化一下:
将内部类或函数设置为私有仅意味着该类或函数的名称只能由该类使用,否则它是不可访问的。在类外部无法编写任何使用私有类或函数名称的代码。
在所示代码中,A::B没有在类外部使用。
出于完全相同的原因,这也适用于其他情况。
class A {

    class B {};

public:

    B foo();
};


int main()
{
    A a;

    auto bar=a.foo();
    return 0;
}

auto可以正常工作。但A::B则不行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接