如何使用C++模块:带模板的私有片段?

问题描述 投票:0回答:2

我正在尝试 C++20,以更好地了解它们在实践中的工作原理。我了解了

module :private
片段,它可用于将接口与实现分开,同时将两者保留在同一个文件中。这似乎适用于标准函数,但不适用于模板函数。

我有以下文件:

// File "main.cpp"

#include <iostream>

import mymodule;

int main()
{
    std::cout << "greeting(): " << mymodule::greetings() << std::endl;
    int x = 1;
    int y = 2;
    std::cout << "add(x, y): " << mymodule::add(x, y) << std::endl;
}
// File "mymodule.ixx"

module;

#include <string>

// declare interface
export module mymodule;

export namespace mymodule {
    template<typename T>
    T add(T x, T y);

    std::string greetings();
}

// implement interface
module :private;

std::string mymodule::greetings() {
    return "hello";
}

template<typename T>
T mymodule::add(T x, T y) {
    return x + y;
}

并获得编译器警告和链接器错误(使用 Visual Studio 2022、MSVC):

Rebuild started...
1>------ Rebuild All started: Project: PlayingWithModules, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>mymodule.ixx
1>Compiling...
1>mymodule.ixx
1>C:\Users\Sam\Development\Cpp\Sandbox\PlayingWithModules\mymodule.ixx(29,13): warning C5226: 'mymodule::add': exported template defined in private module fragment has no reachable instantiation
1>main.cpp
1>main.obj : error LNK2019: unresolved external symbol "int __cdecl mymodule::add<int>(int,int)" (??$add@H@mymodule@@YAHHH@Z::<!mymodule>) referenced in function main
1>C:\Users\Sam\Development\Cpp\Sandbox\x64\Debug\PlayingWithModules.exe : fatal error LNK1120: 1 unresolved externals
1>Done building project "PlayingWithModules.vcxproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

我的理解是

mymodule::greetings()
没问题,但是
mymodule::add(x, y)
不行,因为编译器看不到函数调用
mymodule::<int, int>(x, y)
,导致没有生成函数
<int, int>


如果我将模板函数实现为接口的一部分:

module;

#include <string>

// declare interface
export module mymodule;

export namespace mymodule {
    template<typename T>
    T add(T x, T y) {
        return x + y;
    }

    std::string greetings();
}

// implement interface
module :private;

std::string mymodule::greetings() {
    return "hello";
}

然后一切都会按预期编译并运行。


是否可以将

module :private
与函数模板一起使用,如果可以,如何使用?或者模板函数应该始终作为接口的一部分来实现吗?我无法在线找到有关模块上下文中模板使用的详细信息,也无法找到对我收到的编译器警告的引用。

c++ templates c++20 constexpr c++-modules
2个回答
4
投票

模块不会改变 C++ 的本质,只是改变你访问不同组件的方式。

C++ 本质的一部分是,为了让翻译单元使用模板,该翻译单元必须有权访问该模板的“定义”。模块不会改变这一点。 私有模块片段具体包含“不”属于模块接口的部分。这就是它们的全部要点:能够将不属于界面一部分的东西粘贴到模块文件中。

因此,私有模块片段只能包含您将放入传统 .cpp 文件中的内容。事实上,这就是它们存在的原因:这样(对于短模块)您可以将所有常用的 .cpp 内容放在一个文件中,而不会影响模块生成的内容。

是否可以将

module :private

与函数模板一起使用,如果可以,如何使用?

0
投票
是的!,这是可能的,但前提是您为所需的每种类型显式实例化模板。
显式实例化类似于完整模板专业化的声明,不同之处在于 
省略关键字
template

后面的尖括号

请参阅 CppReference 中的函数模板实例化

类模板实例化 在下面的示例中,我为 add

int

实例化了函数模板

double
(来自 OP)。
// Instantiate `add` for types `int` and `double`.
template
int mymodule::add<int>(int, int);
template
double mymodule::add<double>(double, double);
如果类型可以推导,则可以完全省略 

<int>
<double>

// A more consise way to instantiate the template.
template
int mymodule::add(int, int);
template
double mymodule::add(double, double);
这些显式模板实例化应放置在私有模块片段中,位于函数模板定义的正下方。

接下来是您使用私有模块片段的原始尝试。我唯一添加的是两个显式实例化。

// File "mymodule.ixx" module; #include <string> // declare interface export module mymodule; export namespace mymodule { template<typename T> T add(T x, T y); std::string greetings(); } // implement interface module :private; std::string mymodule::greetings() { return "hello"; } template<typename T> T mymodule::add(T x, T y) { return x + y; } // instantiate template for types `int` and `double` template int mymodule::add(int, int); template double mymodule::add(double, double);

将类型

double
 的测试添加到函数 
main

中可以得到以下结果:

// File "main.cpp"
#include <iostream>
import mymodule;

int main()
{
    std::cout << "greeting(): " << mymodule::greetings() << std::endl;
    int x = 1;
    int y = 2;
    std::cout << "add(x, y): " << mymodule::add(x, y) << std::endl;
    double a = 1.5;
    double b = 0.25;
    std::cout << "add(a, b): " << mymodule::add(a, b) << std::endl;
}
这是输出:

greeting(): hello
add(x, y): 3
add(a, b): 1.75

显式实例化也适用于

.hpp
.cpp
文件
相同的程序可以在没有模块的情况下进行编码。
// mymodule.hpp
#ifndef MYMODULE_HPP
#define MYMODULE_HPP
#include <string>
namespace mymodule
{
    template<typename T>
    T add(T x, T y);

    std::string greetings();
}
#endif

// mymodule.cpp #include <string> #include "mymodule.hpp" std::string mymodule::greetings() { return "hello"; } template<typename T> T mymodule::add(T x, T y) { return x + y; } // instantiate template for type `int` template int mymodule::add<int>(int, int); template double mymodule::add<double>(double, double);

对文件
main.cpp
的唯一更改是删除导入语句,并将其替换为老式的包含指令:

#include "mymodule.hpp"

    

© www.soinside.com 2019 - 2024. All rights reserved.