【问题标题】:How to organize template-functions and functions, in headers and source files如何在头文件和源文件中组织模板函数和函数
【发布时间】:2019-07-05 04:44:33
【问题描述】:

我正在学习使用模板函数并将我的代码组织在多个文件中。我查看了Why can templates only be implemented in the header file?,他们指出我应该在标题中实现我的模板函数;我还查看了C++ inline functions: declare as such, define as such, or both? Why?,所以我知道我应该将完全专用的函数定义为标题中的内联函数;我查看了Why use a “tpp” file when implementing templated functions and classes defined in a header?,他们建议在单独的my.tpp 中定义模板(以及完全专用的模板?)并在我的标题末尾添加#include "my.tpp"

现在我作为一个完全初学者的问题是:如何将所有这些与常规函数结合起来。

想象一下:

#ifndef IVAL_H
#define IVAL_H

//Function to determine if input string is of a given type
template<typename T>
bool is_type(std::string);

//Specialization when testing integer
//As I also want to accept  e.g. 4.0000 as an integer
template<>
bool is_type<int>(std::string);

//Function to get a given type
template<typename T>
T get_type(std::string);

//Finally a normal function to ask for [y/n]
bool yesNo(std::string prompt);

//Include templates definitions
#include"ival.tpp"

#endif /*IVAL_H*/

那么我有,正如上面引用的问题所建议的那样:

//in ival.tpp
#ifndef IVAL_TPP
#define IVAL_TPP

template<typename T>
bool is_type(std::string input)
{
//How to validate input
}

template<>
bool inline is_type<int>(std::string input)
{
\\How to validate when integer
}

template<typename T>
T get_type(std::string prompt)
{
//How to keep asking until valid input
//Using is_type
}
#endif /*IVAL_H*/

最后作为.cpp 我的正常功能:

//in ival.cpp
#include "ival.h"

bool yesNo(std::string prompt)
{
//How to ask for [y/n]
//Using get_type<char>
}

这会导致一些关于如何组织我的功能的正确方法的混乱。当在标题中我们有模板函数和普通函数时,做我上面所做的正常的事情吗? (普通函数的不同源文件),是完全专用的函数,被视为模板(即在所有模板所在的文件中内联定义)或函数(即仅在 .cpp 中定义为所有其他函数)。

在 .cpp 中定义模板函数并显式实例化为 char, int, double, floatstd::string 是不是更方便

【问题讨论】:

  • 没有完美的方法来解决这个问题,您的解决方案似乎很好。
  • 标头中的模板函数定义/主体,代码文件中的普通函数定义/主体不仅是常见的,而且是必要的。请详细说明不清楚的地方。
  • 如果你完全特化一个函数模板,你会得到一个常规函数,它的定义在源文件中。
  • @FrançoisAndrieux 这与将其作为内联在标题中与其余模板有什么区别?为什么一个比另一个更方便?
  • @DanielDuque 任何你放在头文件中的东西,如果你改变了它,你将需要重新编译 #include 的那个头文件,或者包含一个包含那个头文件的文件,等等。如果您更改源文件中的某些内容(其他文件不应包含的文件),那么您只需重新编译该文件并重新链接所有内容。尽可能多地放在源文件中以限制编译时间。编辑:一些编译器可能很难在源文件之间进行优化,所以其他人可能会认为内联函数(在头文件中)更适合优化(我不同意)。

标签: c++ function code-organization function-templates


【解决方案1】:

您的解决方案对我来说看起来不错。通常可以在文件 ival.h 的末尾插入文件 ival.tpp 的代码,这样就只有一个头文件了。

如何将所有这些与常规函数结合起来。

通常的规则是:

  • 仅将常规函数的定义放入 *.cpp 文件中。
  • 将所有模板函数定义、内联函数定义和常规函数声明放入您的 *.h 文件(或者有些人喜欢称其为 *.hpp)。选择一个顺序,以便大多数函数只使用/调用它们上面定义的函数。
  • 仅在必要时(例如,循环依赖项):在 *.h 文件的最顶部放置足够的模板和内联函数声明,以便在调用所有被调用函数之前声明它们。这通常只在特殊情况下才需要。

当我们在一个标题中我们有模板函数和普通函数时,做我上面所做的正常的事情吗?

通常不需要在定义模板函数之前显式声明它们,因此这通常被省略。这通常只在少数必要的情况下进行,即当模板函数相互引用/调用时。调用图中甚至可以存在循环。只需将所有内容声明为内联,并将其实际内联的内容留给编译器。

将完全专用的函数视为模板还是函数?

将它们视为模板。所有能看到泛型模板定义的代码也应该能看到特化。否则事情会变得一团糟(程序的不同部分对调用的同一个函数使用不同的代码)。

与在 .cpp 中定义模板函数并显式实例化为 char、int、double、float 和 std::string 相比,我所做的是否更方便?

没有。除非您有具体的理由,否则不要使用显式模板实例化。不要为了保持头文件精简而使用显式模板实例化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多