为什么这个构造函数没有报“不完整类型错误”?

13

我有两个文件test.h和main.cpp,如下所示:

test.h

#include <memory>

class TestImpl;

template <typename... T>
void createConnection(T&&... Args)
{
    // 1. Why is this working if the constructor is in cpp?
    std::unique_ptr<TestImpl> pimpl(new TestImpl(std::forward<T>(Args)...));
    std::cout << "Done..." << std::endl;

    // 2. Why is this not working if the constructor call has no issues?
    pimpl->sayHello();
}

main.cpp

#include <iostream>

#include "test.h"

class TestImpl
{
public:
    TestImpl(const std::string& first, const std::string& second)
        : _first(first)
        , _second(second)
    {
    }

    void sayHello()
    {
        std::cout << "Hello ... " << std::endl;
    }

private:
    std::string _first;
    std::string _second;
};

int main()
{
    std::cout << "Hello World!" << std::endl;
    createConnection("ABC", "DEF");
    return 0;
}

从评论中可以明显看出,我的主要问题是为什么构造函数调用没有报错“无效使用不完整类型'class TestImpl'...”。参考资料是我使用的GCC 5.2,没有特定的标志。


5
在模板实例化的时候,它确实是不完整的吗? - Evg
1
原因是您在模板中使用了构造函数,当模板被实例化并且此时编译器已经知道了 TestImpl 的完整定义。 - Marek R
2
不可复现 - n. m.
1
我尝试复现但失败了。GCC 5.2 on Wandbox 拒绝了它。TU的布局与您的示例完全相同。 - StoryTeller - Unslander Monica
2
@StoryTeller 是的,你的示例在构造函数中有效,但在sayHello方法中无效 - 这正是问题所在的意外行为。@n.m. 也是如此。 - V0ldek
显示剩余11条评论
1个回答

10
简单来说,GCC不必拒绝你的程序,Clang也不必接受它。由于TestImpl是不完整的,因此您的模板违反了[temp.res]/8。这是一种错误形式,无需诊断。

[temp.res]/8

……如果:

  • 紧跟其定义的一个模板的假设实例化,由于构造而出现非格式,则该程序形式不正确,无需诊断,或
  • 在假设实例化的解释中,这样的结构与模板的任何实际实例化中相应结构的解释不同。
可以认为正在调用的构造函数是相关的,但类名肯定不是!在我们的案例中,使用两个字符串的假设实例化紧跟在模板定义之后将产生与程序中实例化点处不同的结果。 这是因为类名本身(再次强调,不相关)在两个上下文中具有不同的含义。这不是有效的模板定义。但是,GCC在这里行使了一些自由裁量权,因为没有必要进行诊断,而且继续前进。

以下是简洁总结,虽然不是规范的,但描述了您的情况:

在以下情况下可能会发生这种情况:

  • 在定义模板时,非相关名称中使用的类型在该点上是不完整的,但在执行实例化时是完整的;或者

8.5.1 更为直接。 - xskxzr
@xskxzr - 是的。但我在Note处停止阅读 : )。我会添加它。 - StoryTeller - Unslander Monica
@Oliv - 一个函数对象可能适合这种形式,但我认为它不会是一个函数调用表达式本身。不过我也不确定。 - StoryTeller - Unslander Monica
@Oliv 后缀表达式中仅包含未限定标识符的内容远不止函数调用,还包括函数对象、使用类型名称、模板名称等创建临时变量等等。 - Passer By
我不确定gcc是错的。仅考虑语法而非语义,后缀表达式可以是主表达式,也就是名称,因此是标识符。在[dep.name]中提到的“形式”可能是在强调这是一条语法规则,如果是这样的话,那么gcc就是正确的,clang就是错误的。 - Oliv
显示剩余6条评论

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