Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this constructor not giving an incomplete type error?

I have two files test.h and main.cpp as shown below:

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;
}

As evident from the comments my main question is why the constructor call is not giving an error "invalid use of incomplete type 'class TestImpl'...". For reference I am using GCC 5.2, with no specific flags.

like image 251
PapaDiHatti Avatar asked Aug 20 '18 13:08

PapaDiHatti


1 Answers

Simply put, GCC doesn't have to reject your program, and Clang doesn't have to accept it. It's ill-formed, no diagnostic required. Since TestImpl is incomplete, your template is in violation of

[temp.res]/8

... The program is ill-formed, no diagnostic required, if:

  • a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
  • the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template.

One could argue that the constructor being called is dependent, but the class name is surely not!

In our case, a hypothetical instantiation with a pack of two strings immediately after the template definition will give different results than at the point of instantiation in your program. This is because the class name itself (which is, again, not dependent) has different meaning in the two contexts.

It's not a valid template definition. But GCC is exercising some leeway here, since no diagnostic is required, and plowing on.


This is succinctly summarized in the note under the bullets, which while not normative, describes your case:

This can happen in situations including the following:

  • a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is performed, or
like image 147
StoryTeller - Unslander Monica Avatar answered Oct 07 '22 01:10

StoryTeller - Unslander Monica