为什么我可以在decltype()中使用私有默认构造函数

Why can I use private default constructor in decltype()?

本文关键字:默认 构造函数 我可以 decltype 为什么      更新时间:2023-10-16

查看代码:

#include <iostream>
#include <utility>
class test
{
private:
    test() { }
public:
    test foo() { return *this; }
    static const char *name() { return "test"; }
};
int main()
{
    std::cout << decltype(test().foo())::name() << std::endl;               // 1
    std::cout << decltype(std::declval<test>().foo())::name() << std::endl; // 2
}

我希望// 1行无法编译,因为test的默认构造函数是私有的。

然而,它运行良好。我难以置信地在我的g++4.8.3上用-Wall -Wextra -Werror -pedantic测试了它,但它运行良好,没有任何错误或警告。

(此外,它似乎在GCC 4.9.1中也能很好地工作。)

从这个页面,我想如果表达式未赋值,我们可以使用私有默认构造函数。所以,我测试了以下内容来检查它。

#include <iostream>
#include <utility>
class test
{
private:
    test(int) { }
public:
    test foo() { return *this; }
    static const char *name() { return "test"; }
};
int main()
{
    std::cout << decltype(test().foo())::name() << std::endl;               // 1
    std::cout << decltype(std::declval<test>().foo())::name() << std::endl; // 2
}

(实例)

不出所料,它没有被编译。

但是为什么这怎么可能?我们可以在未赋值的表达式中使用私有成员吗?或者对于默认构造函数有一个特殊的规则吗?你能解释一下为什么吗?

它不应该编译。C++11[class.temporary]对创建一个临时对象有这样的看法:

12.2/1即使临时对象的创建是未评估的或以其他方式避免,所有语义限制都应得到尊重,就好像临时对象已经创建并随后被销毁一样。[注意:即使没有对析构函数或复制/移动构造函数的调用,也应满足所有语义限制,如可访问性和函数是否被删除。但是,在函数调用用作decltype说明符的操作数的特殊情况下,不会引入临时性,因此上述内容不适用于任何ch函数调用。--尾注]

因此,即使未评估,您仍然受到创建和销毁临时所需的任何函数(包括构造函数)的可访问性的限制。注释的最后一句阐明了像declval这样的函数可以用来避免这个障碍。