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\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
Added as being required after solved:
If you rewrite the above in the all old #include
fashion, MSVC will show:
error C2600: "Person::~Person": Cannot define special compiler-generated member functions (must first be declared in the class)
It's like Forward declaration with unique_ptr.