c++ 为什么Person::Impl未定义?[副本]

imzjd6km  于 2023-06-07  发布在  其他
关注(0)|答案(1)|浏览(149)

此问题已在此处有答案

What is an undefined reference/unresolved external symbol error and how do I fix it?(39答案)
23天前关闭
截至23天前,社区正在审查是否重新讨论这个问题。
我试着做一个关于 Professional C++ 第5版的练习,遇到了一个问题。我已经在Person.cpp中定义了Person::Impl,但是MSVC总是显示它是未定义的。
我有这些文件:

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());

我看过书的作者Marc的答案,但我很遗憾地没有找到它的错误之处(不完全是在辅导之后,见下文),因为C++模块似乎太新了,我的答案和Marc的答案之间没有任何区别。
整个错误消息是:

[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\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 "STD: : unique_ptr < Person: : Impl, STD: : default_delete < 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(3065,25): error C2338: static_assert failed:  'can't delete an incomplete type' [C:\Users\leen\workspace\cpp\professional_c++_answer\chapter-9\9-4\build\9-4.vcxproj]
[build] Build finished with exit code -1

解决后根据需要增加:

如果你用旧的#include方式重写上面的代码,MSVC将显示:

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

就像 Forward declaration with unique_ptr

qij5mzcb

qij5mzcb1#

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

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

这种情况类似于前模块世界中的this code。如果析构函数未包含在类定义中,编译器将假定您需要编译器定义的隐式析构函数。这将失败,因为析构函数需要在main.cpp的上下文中生成,其中类型Person::Impl未知。
MSVC按原样接受代码,在模块实现中有析构函数定义,但在类定义中没有声明析构函数,这一事实在我看来很奇怪。这实际上可能是一个编译器错误(尽管我必须首先深入研究标准措辞来确认)。
或者,您也可以将Person::Impl的整个类定义移动到主模块接口中,而不导出它。person的客户端仍然无法示例化该类,因为它被声明为private嵌套类,但它将为编译器提供足够的信息,以便在main.cpp的上下文中示例化~Person()

相关问题