Person::Impl 为何未定义?

huangapple go评论73阅读模式
英文:

Why is Person::Impl undefined?

问题

I was trying to do an exercise on Professional C++ 5th edition, and I met a problem. I have defined Person::Impl in Person.cpp, but MSVC always shows me it's undefined.

I have these files:

CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.26)

set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP ON)

set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${CMAKE_PROJECT_NAME})

set(CMAKE_CXX_STANDARD 23)
project(9-4)

add_executable(${CMAKE_PROJECT_NAME})
target_sources(${CMAKE_PROJECT_NAME}
    PUBLIC
    FILE_SET all_my_modules TYPE CXX_MODULES FILES
    main.cpp
    Person.cppm
    PRIVATE
    Person.cpp
)

Person.cppm

export module person;

import <string>;
import <memory>;

export class Person{
    public:
        Person(const std::string_view firstName,const std::string_view lastName);
        Person(const std::string_view firstName,const std::string_view lastName,const char firstUnitOfName);
        Person()=default;
        std::string getFirstName() const;
        std::string getLastName() const;
        char getFirstUnitOfName() const;
        void setFirstName(const std::string_view firstName);
        void setLastName(const std::string_view lastName);
        void setFirstUnitOfName(const char c);
    private:
        class Impl;
        std::unique_ptr<Impl> m_impl;
};

Person.cpp

module person;

import <string>;

using namespace std;

class Person::Impl
{
private:
    string m_FirstName;
    string m_LastName;
    char m_FirstUnitOfName;

public:
    Impl() = default;
    Impl(const string_view firstName, const string_view lastName) : m_FirstName(firstName), m_LastName(lastName), m_FirstUnitOfName(firstName.front()){};
    Impl(const string_view firstName, const string_view lastName,const char c) : m_FirstName(firstName), m_LastName(lastName), m_FirstUnitOfName(c){};
    string getFirstName() const
    {
        return m_FirstName;
    };
    string getLastName() const
    {
        return m_LastName;
    };
    char getFirstUnitOfName() const
    {
        return m_FirstUnitOfName;
    };
    void setFirstName(const string_view firstName)
    {
        m_FirstName = firstName;
    }
    void setLastName(const string_view lastName)
    {
        m_LastName = lastName;
    }
    void setFirstUnitOfName(const char c)
    {
        m_FirstUnitOfName = c;
    }
};

Person::Person(const std::string_view firstName, const std::string_view lastName) : m_impl{make_unique<Impl>(firstName, lastName)} {};
Person::Person(const string_view firstName, const string_view lastName,const char firstUnitOfName) : m_impl{make_unique<Impl>(firstName, lastName,firstUnitOfName)}{};

std::string Person::getFirstName() const{
    return m_impl->getFirstName();
};
std::string Person::getLastName() const{
    return m_impl->getLastName();
};
char Person::getFirstUnitOfName() const{
    return m_impl->getFirstUnitOfName();
};
void Person::setFirstName(const std::string_view firstName){
    m_impl->setFirstName(firstName);
};
void Person::setLastName(const std::string_view lastName){
    m_impl->setLastName(lastName);
};
void Person::setFirstUnitOfName(const char c){
    m_impl->setFirstUnitOfName(c);
};
Person::~Person() = default;

main.cpp

import <iostream>;
import <format>;
import <string>;
import person;
using namespace std;

int main(){
    Person testman("Test","Man");
    cout<<format("The man's firstname is {},lastname is {},first unit is {}.\n",testman.getFirstName(),testman.getLastName(),testman.getFirstUnitOfName());
    testman.setFirstName("Man");
    testman.setLastName("Test");
    testman.setFirstUnitOfName('T');
    cout<<format("The man's firstname is {},lastname is {},first unit is {}.\n",testman.getFirstName(),testman.getLastName(),testman.getFirstUnitOfName());

I have seen the book writer Marc's answer, but I sadly failed to find where it is wrong(not exactly after being tutoring, see below), because C++ modules seem to be too new, and there isn't any difference between mine and Marc's answer.

The whole error message is:

[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3065,1): error C2027: Used the undefined type "Person::Impl" [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\Person.cppm(18,15): message: See the declaration of "Person::Impl" [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3064,1): message: When compiling class template member functions "void std::default_delete<Person::Impl>::operator ()(_Ty *) noexcept const" [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build]           with
[build]           [
[build]               _Ty=Person::Impl
[build]           ]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3177,33): message: View a reference to the function template instantiation "void std::default_delete<Person::Impl>::operator ()(_Ty *) noexcept const" being compiled [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build]           with
[build]           [
[build]               _Ty=Person::Impl
[build]           ]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3101,1): message: View a reference to the class template instantiation "std::default_delete<Person::Impl>" being compiled [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build
<details>
<summary>英文:</summary>
I was trying to do an exercise on *Professional C++* 5th edition, and I met a problem. I have defined `Person::Impl` in `Person.cpp`, but [MSVC][1] always shows me it&#39;s undefined.
I have these files:
### *CMakeLists.txt*
```cmake
CMAKE_MINIMUM_REQUIRED(VERSION 3.26)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API &quot;2182bf5c-ef0d-489a-91da-49dbc3090d2a&quot;)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP ON)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${CMAKE_PROJECT_NAME})
set(CMAKE_CXX_STANDARD 23)
project(9-4)
add_executable(${CMAKE_PROJECT_NAME})
target_sources(${CMAKE_PROJECT_NAME}
PUBLIC
FILE_SET all_my_modules TYPE CXX_MODULES FILES
main.cpp
Person.cppm
PRIVATE
Person.cpp
)

Person.cppm

export module person;

import &lt;string&gt;;
import &lt;memory&gt;;

export class Person{
    public:
        Person(const std::string_view firstName,const std::string_view lastName);
        Person(const std::string_view firstName,const std::string_view lastName,const char firstUnitOfName);
        Person()=default;
        std::string getFirstName() const;
        std::string getLastName() const;
        char getFirstUnitOfName() const;
        void setFirstName(const std::string_view firstName);
        void setLastName(const std::string_view lastName);
        void setFirstUnitOfName(const char c);
    private:
        class Impl;
        std::unique_ptr&lt;Impl&gt; m_impl;
};

Person.cpp

module person;

import &lt;string&gt;;

using namespace std;

class Person::Impl
{
private:
    string m_FirstName;
    string m_LastName;
    char m_FirstUnitOfName;

public:
    Impl() = default;
    Impl(const string_view firstName, const string_view lastName) : m_FirstName(firstName), m_LastName(lastName), m_FirstUnitOfName(firstName.front()){};
    Impl(const string_view firstName, const string_view lastName,const char c) : m_FirstName(firstName), m_LastName(lastName), m_FirstUnitOfName(c){};
    string getFirstName() const
    {
        return m_FirstName;
    };
    string getLastName() const
    {
        return m_LastName;
    };
    char getFirstUnitOfName() const
    {
        return m_FirstUnitOfName;
    };
    void setFirstName(const string_view firstName)
    {
        m_FirstName = firstName;
    }
    void setLastName(const string_view lastName)
    {
        m_LastName = lastName;
    }
    void setFirstUnitOfName(const char c)
    {
        m_FirstUnitOfName = c;
    }
};

Person::Person(const std::string_view firstName, const std::string_view lastName) : m_impl{make_unique&lt;Impl&gt;(firstName, lastName)} {};
Person::Person(const string_view firstName, const string_view lastName,const char firstUnitOfName) : m_impl{make_unique&lt;Impl&gt;(firstName, lastName,firstUnitOfName)}{};

std::string Person::getFirstName() const{
    return m_impl-&gt;getFirstName();
};
std::string Person::getLastName() const{
    return m_impl-&gt;getLastName();
};
char Person::getFirstUnitOfName() const{
    return m_impl-&gt;getFirstUnitOfName();
};
void Person::setFirstName(const std::string_view firstName){
    m_impl-&gt;setFirstName(firstName);
};
void Person::setLastName(const std::string_view lastName){
    m_impl-&gt;setLastName(lastName);
};
void Person::setFirstUnitOfName(const char c){
    m_impl-&gt;setFirstUnitOfName(c);
};
Person::~Person() = default;

main.cpp

import &lt;iostream&gt;;
import &lt;format&gt;;
import &lt;string&gt;;
import person;
using namespace std;

int main(){
    Person testman(&quot;Test&quot;,&quot;Man&quot;);
    cout&lt;&lt;format(&quot;The man&#39;s firstname is {},lastname is {},first unit is {}.\n&quot;,testman.getFirstName(),testman.getLastName(),testman.getFirstUnitOfName());
    testman.setFirstName(&quot;Man&quot;);
    testman.setLastName(&quot;Test&quot;);
    testman.setFirstUnitOfName(&#39;T&#39;);
    cout&lt;&lt;format(&quot;The man&#39;s firstname is {},lastname is {},first unit is {}.\n&quot;,testman.getFirstName(),testman.getLastName(),testman.getFirstUnitOfName());

I have seen the book writer Marc's answer, but I sadly failed to find where it is wrong(not exactly after being tutoring, see below), because C++ modules seem to be too new, and there isn't any difference between mine and Marc's answer.

The whole error message is:

[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3065,1): error C2027: Used the undefined type &quot;Person::Impl&quot; [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\Person.cppm(18,15): message: See the declaration of &quot;Person::Impl&quot; [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3064,1): message: When compiling class template member functions &quot;void std::default_delete&lt;Person::Impl&gt;::operator ()(_Ty *) noexcept const&quot; [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build]           with
[build]           [
[build]               _Ty=Person::Impl
[build]           ]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3177,33): message: View a reference to the function template instantiation &quot;void std::default_delete&lt;Person::Impl&gt;::operator ()(_Ty *) noexcept const&quot; being compiled [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build]           with
[build]           [
[build]               _Ty=Person::Impl
[build]           ]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3101,1): message: View a reference to the class template instantiation &quot;std::default_delete&lt;Person::Impl&gt;&quot; being compiled [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\main.cpp(8,1): message: View class template instantiation of are compiling &quot;STD: : unique_ptr &lt; Person: : Impl, STD: : default_delete &lt; Person: : Impl &gt; &gt;&quot; [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] C:\Environment\VisualStudio\2022\Community\VC\Tools\MSVC\14.35.32215\include\memory(3065,25): error C2338: static_assert failed:  &#39;can&#39;t delete an incomplete type&#39; [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] Build finished with exit code -1

<hr/>

Added as being required after solved:

If you rewrite the above in the all old #include fashion, MSVC will show:

error C2600: &quot;Person::~Person&quot;: Cannot define special compiler-generated member functions (must first be declared in the class)

It's like Forward declaration with unique_ptr.

答案1

得分: 1

你需要将析构函数的声明添加到Person类的定义中:

Person.cpp

export class Person{
    public:
        ~Person();
    [...]

这种情况类似于此代码,在预模块时代。如果析构函数没有包含在类定义中,编译器会假定你想要一个由编译器定义的隐式析构函数。这会导致错误,因为该析构函数需要在main.cpp的上下文中生成,而在那里类型Person::Impl是未知的。

我觉得微软的编译器接受你的代码而不在类定义中声明析构函数,但在模块实现中定义析构函数,这看起来很奇怪。这可能实际上是一个编译器bug(尽管我需要先查阅标准文档来确认)。

或者,你也可以将整个Person::Impl类定义移到主模块接口中,但不导出它。person的客户端仍然无法实例化该类,因为它被声明为private嵌套类,但它将为编译器提供足够的信息,以在main.cpp的上下文中实例化~Person()

英文:

You need to add the declaration for the destructor to the class definiton of Person:

Person.cppm

export class Person{
public:
~Person();
[...]

The situation is similar to this code in a pre-modules world. If the destructor is not included in the class definition, the compiler will assume that you want a compiler-defined implicit destructor. This will fail, because that destructor would need to be generated in the context of main.cpp, where the type Person::Impl is not known.

The fact that MSVC accepted your code as-is, with the destructor definition in the module implementation, but no declaration of the destructor in the class definition looks weird to me. This might actually be a compiler bug (though I'll have to dig through the standard wording first to confirm).

Alternatively, you can also move the entire class definition for Person::Impl into the primary module interface without exporting it. Clients of person will still not be able to instantiate that class because of it being declared as a private nested class, but it will provide sufficient information for the compiler to instantiate ~Person() in the context of main.cpp.

huangapple
  • 本文由 发表于 2023年5月14日 20:39:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76247529.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定