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

9

看一下代码:

#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的默认构造函数是私有的。

然而,它可以正常工作。 我用-Wall -Wextra -Werror -pedantic在我的g++ 4.8.3上进行了测试,并感到不可思议,但是它可以正常工作,没有任何错误或警告。

(此外,在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
}

(现场示例)

正如预料的那样,它没有被编译。

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


在clang中无法编译。 - T.C.
@T.C. 真的吗?那可能只是 GCC 的 bug。 - ikh
1
嗯,C++98没有 decltype。或者 declval。当然它不会编译。 - T.C.
@T.C. Oh, 我的大脑被搞糊涂了;; - ikh
哦,它甚至可以在GCC 4.9.1中编译... - ikh
这似乎是一个 g++ 的 bug,clang 无法编译,你可以在这里查看:http://coliru.stacked-crooked.com/a/9bdd0af1734740d3 - Jamboree
1个回答

13
它不应该被编译。C++11 [class.temporary] 对于创建临时对象的规定如下:
12.2/1 即使避免了临时对象的创建或者说是在“未求值表达式”的情况下,所有语义限制也必须被遵守,就像临时对象已经被创建并稍后销毁一样。[注:即使没有调用析构函数或拷贝/移动构造函数,所有语义限制(例如可访问性和函数是否已删除)都必须被满足。但是,在将函数调用用作decltype-specifier的操作数的特殊情况下,不会引入任何临时对象,因此前述内容不适用于任何此类函数调用的prvalue。 — end note]
因此,即使在未求值表达式中,您仍受到所需创建和销毁临时对象的任何函数(包括构造函数)可访问性的限制。备注的最后一句澄清了像declval这样的函数可以用来避免这个障碍。

1
谢谢。+1给最后一句话。(哦,我发现我不能在一分钟内接受答案 >o<) - ikh
那一定是gcc的bug了,我要不要报告一下?>o< - ikh
结尾注释显示:decltype-specifier,不会引入临时对象,因此前面的内容不适用于任何这样的函数调用的prvalue。如何理解这一部分? - Kevin eyeson

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