我有一个c ++模板类,只有在模板化类型是普通旧数据时才能正常运行.任何具有执行任何操作的构造函数的东西都无法正常工作.
无论如何,当有人试图这样做时,我想以某种方式获得编译时或运行时警告.
//this should generate error
myclass<std::string> a;
//this should be fine
myclass<int> b;
Run Code Online (Sandbox Code Playgroud)
有这个诀窍吗?
根据我的经验,有许多代码明确使用内联函数,这需要权衡:
问题是:链接时优化(例如,在GCC中)是否呈现手动内联,例如,在C99中声明一个"内联"函数并提供一个实现,已经过时了?我们是否真的不需要考虑自己内联大多数函数?那些总是从内联中受益的函数呢,例如deg_to_rad(x)?
澄清:我不是在考虑同一个翻译单元中的函数,而是考虑逻辑上应该存在于不同翻译单元中的函数.
更新:我经常看到反对"内联",并建议过时.但是,就个人而言,我确实经常看到明确的内联函数:作为类体中定义的函数.
如本答案中所述,模板实例化允许减少编译时间和大小,因为不需要为使用它们的每个新文件中的每个新类型重新编译模板。
我也很高兴C++20 模块应该如何提供一个干净的解决方案来向外部项目公开模板并减少 hpp/cpp 重复。
允许它们一起工作的语法是什么?
例如,我希望模块看起来有点像(未经测试,因此可能是错误的代码,因为我没有足够新的编译器/不确定它是否已实现):
helloworld.cpp
export module helloworld;
import <iostream>;
template<class T>
export void hello(T t) {
std::cout << t << std::end;
}
Run Code Online (Sandbox Code Playgroud)
helloworld_impl.cpp
export module helloworld_impl;
import helloworld;
// Explicit instantiation
template class hello<int>;
Run Code Online (Sandbox Code Playgroud)
主程序
// How to prevent the full definition from being imported here, which would lead
// hello(1) to instantiate a new `hello<int>` instead of reusing the explicit instantiated
// one from `helloworld_impl.cpp`?
import helloworld;
int main() {
hello(1); …Run Code Online (Sandbox Code Playgroud) 通常,当我创建一个类时,我会为该类创建一个标头和一个源.我听说过使用模板类,你必须将函数实现放在头文件中.我试过两种方式,并从第一种方式得到编译错误.第二种方式很好.但是,我喜欢将代码组织到头文件和源文件中,那么是否可以将函数实现放入源文件中?(也许它需要特殊的编译标志或语法?)或者我应该将em保留在标题中?
谢谢!
我已经参考了cplusplus.com上的Explicit Template Instantiation文章,该文章给出了以下示例:
template <typename T> class Example
{
public:
Example( T test )
{
_data = test;
}
void setTest(T test)
{
_data = T;
}
private:
T _data;
};
class template Example<int>;
class template Example<float>;
class template Example<double>;
Run Code Online (Sandbox Code Playgroud)
除了我试图将一个类型分配给成员变量_data = T而不是我认为应该是什么之类的东西看起来像是一个遗漏错误_data = test- 我不明白的是最后3行声明或指示的是什么编译器要做什么?
我知道模板是什么,用它们构建程序,并且一般都知道它们的实例化和专业化.我对后两者的理解可能有一些漏洞,但我通常使用例如template class Example<int>;表单而不是片段中显示的那个来指示显式模板实例化.
我已经尝试使用编译片段g++ -std=c++11 -pedantic,它编译得很好而没有警告(我先纠正了_date = T上面的错误).
在我对一个相关问题的答案进行评论之后,我仍然不确定该片段中最后3行中的任何一行是模板特化还是实例化.
(Oktalist在下面给出了一个很好的答案,查看它下面的评论,以帮助演示我们讨论的所有内容,我在问题的底部添加了一个完整的编译解决方案,演示了所讨论的所有内容.)
我有一组命名空间全局方法和模板化方法,如下所示:
namespace PrettyPrint
{
String to_string(bool val);
String to_string(char val);
String to_string(int val);
String to_string(uint val);
// ETC...
template <typename T> String to_string(const T* val)
{
if (! val) return U("NULL");
return String().copy_formatted("(%p)-> %S", (const void*)val, to_string(*val).uchars());
}
// ... and more templates to help with containers and such
}
Run Code Online (Sandbox Code Playgroud)
"String"类型不是C++字符串,它是从IBM的ICU库派生的特殊类,但与此问题并不真正相关.重点是,我有一堆称为to_string的命名空间全局方法,还有一些覆盖它们的模板化函数.到目前为止,这么好,一切都很好.但是,现在我有另一个标题,我有类似下面的定义:
namespace User
{
struct Service {
int code;
String name;
}
//...
}
namespace PrettyPrint
{
String to_string(const User::Service& val) { return val.name; }
}
Run Code Online (Sandbox Code Playgroud)
所以,现在我已经在其他地方定义了一些其他类型,我还在PrettyPrint命名空间中定义了另一个to_string覆盖,以指示如何将我的新类型转换为String.将两个标题放入文件中,如下所示:
#include <the …Run Code Online (Sandbox Code Playgroud) 最近,当我尝试优化自己的包含层次结构时,偶然发现了该文件a.hpp:
template<class T>
class A
{
using t = typename T::a_t;
};
class B;
extern template class A<B>;
Run Code Online (Sandbox Code Playgroud)
这似乎是错误的形式。实际上,似乎extern模板语句的末尾似乎导致实例化A<B>,导致编译器抱怨类型不完整。
我的目标将是确定A<B>的a.cpp:
#include <b.hpp>
template class A<B>;
Run Code Online (Sandbox Code Playgroud)
这样,我避免必须包括b.hpp来自a.hpp这似乎是一个好主意,以减少编译时间。但是,它不起作用(a.hpp本身无法编译!)是否有更好的方法呢?
注意:当然,我不能只使用显式模板实例化,但这不是我想要的!我想“预编译” A<B>以节省编译时间(如果已使用),但如果A<B>未使用,我不想b.hpp在每个使用a.hpp!的文件中都包括在内!
我正在研究一个C++程序,来自一个1200行文件(初始化一个相当复杂的状态机)的编译对象代码几乎达到了一兆字节.什么可以使文件这么大?有没有办法找到目标文件中需要空间的内容?
我正在维护一个大型的模板类库,它们可以根据任何一个float或double类型执行代数计算.许多类都有访问器方法(getter和setter)以及运行少量代码的其他函数,因此当编译器定位它们的定义时,需要将这些函数限定为内联.相反,其他成员函数包含复杂的代码,因此最好调用而不是内联.
函数定义的很大一部分位于头文件中,实际上是头文件所包含的.inl文件.但也有很多类,其功能定义,幸福地生活在.cpp文件通过显式实例化的手段float和double,这是相当的好东西在图书馆的情况下做的(这里解释为什么).最后,有相当多的类,它们的函数定义被分解为.inl文件(访问器方法)和.cpp文件(构造函数,析构函数和繁重的计算),这使得它们都很难维护.
只有当我知道一种可靠的方法来防止某些函数被内联时,或者在.cpp文件中,如果inline关键字可以强烈建议编译器内联一些函数,我当然会在.inl文件中实现所有类实现,当然,它才不是.我真的更喜欢库中的所有函数定义都驻留在.cpp文件中,但由于访问器方法在整个库中被广泛使用,所以我必须确保它们在被引用时被内联,而不是被调用.
所以,在这方面,我的问题是:
inline考虑到这样一个事实,标记模板函数的定义是否有意义,正如我最近在这里学到的那样,它将被编译器自动限定为内联,无论它是否标记inline?
而最重要的,因为我想有聚集了模板类的所有成员函数的定义在一个文件一起,要么(使用显式实例中的.cpp的情况下),最好是.INL或的.cpp 仍然能够提示编译器(MSVC和GCC)应该内联哪些函数,哪些不应该,确定模板函数是否可以实现,如何实现这一点,或者如果真的没有办法(我希望有的话) ),最佳折衷方案是什么?
EDIT1:我知道inline关键字只是建议编译器内联函数.
EDIT2:我真的知道.我喜欢向编译器提出建议.
EDIT3:我还是知道.这不是问题所在.
鉴于一些新的信息,还有第三个问题与第二个问题密切相关.
3.如果编译器如此聪明,以至于他们可以更好地选择应该内联哪个函数以及应该调用哪个函数,并且能够进行链接时代码生成和链接时优化,这有效地允许他们查看.cpp -located函数定义在链接时决定其内联或调用的命运,那么一个好的解决方案可能只是将所有定义移动到相应的.cpp文件中?
那么结论是什么?
首先,我很感谢Daniel Trebbien和Jonathan Wakely的结构和有根据的答案.投票两者但必须只选择一个.然而,给出的答案都没有给我一个可接受的解决方案,所以所选择的答案恰好是帮助我做出最终决定的其他答案,其中的细节将在下一个对任何感兴趣的人解释.
好吧,因为我一直在重视代码的性能而不是维护和开发的方便性,在我看来,最可接受的折衷方案是移动所有的访问器方法和其他每个的轻量级成员函数.将模板类放入相应标头所包含的.inl文件中,用inline关键字标记这些函数,以试图为编译器提供良好的提示(或使用关键字进行内联强制),并将其余的函数移动到相应的.cpp文件.
将所有成员函数定义放在.cpp文件中会妨碍内容轻量级函数,同时释放链接时优化的一些问题,正如Daniel Trebbien为MSVC(在较旧的开发阶段)和Jonathan Wakely为GCC所确定的那样(在目前的发展阶段).并且将所有函数定义放在头文件(或.inl文件)中并不会超过将每个类的实现分类为.inl和.cpp文件以及此决策的额外副作用的总体好处:它将确保只有原始访问器方法的代码对于库的客户端是可见的,而更复杂的东西隐藏在二进制文件中(确保这不是一个主要原因,但是这对于熟悉软件库的人来说是显而易见的. ).并且任何不需要通过库的包含文件公开并且由其类私有使用的轻量级成员函数可以在类的.cpp文件中定义,而其声明/定义inline用于鼓励函数的内联状态(在这个特定情况下,不知道关键字是在两个地方还是只有一个).
我在课堂上有一个模板方法:
template<class T>
A& doSomething(T value)
Run Code Online (Sandbox Code Playgroud)
然后我有一个实现
template<class T>
A& A::doSomething(T value){
// do something with value
return *this;
}
Run Code Online (Sandbox Code Playgroud)
然后,我有一个专业,让我们说bool
template<>
A& A::doSomething(bool value){
// do something special with value of type bool
return *this
}
Run Code Online (Sandbox Code Playgroud)
这是我的理解,现在在代码中有类似的东西我不知道是什么意思:
template A& A::doSomething(char const*);
template A& A::doSomething(char);
template A& A::doSomething(int);
template A& A::doSomething(int*);
template A& A::doSomething(double);
...
Run Code Online (Sandbox Code Playgroud)
具有模板的最后5行的确切含义是什么?
谢谢
我想编写模板函数,只要在类型为整数时调用它,例如int8,int16,int32,int64,uint8,uint16,uint32,uint64; 和另一个模板函数(同名),不同的代码只适用于FLOAT类型float32,float64 ...无论如何使用C++中的模板函数来做到这一点?例如 :
template <class T>
void fun(T b)
{
/// Code for integer type
}
template <class S>
void fun(S a)
{
/// Code for floating type
}
Run Code Online (Sandbox Code Playgroud) 代码示例:
#include <cstdio>
class Test
{
public:
template<typename T, typename T2>
bool operator =(T& value)
{
return true;
}
};
template bool Test::operator= <int, bool>(int&);
int main()
{
Test t;
int gg = 1234;
t = gg;
}
Run Code Online (Sandbox Code Playgroud)
检查了几个编译器。线 t=gg 的问题。如果我删除第二个模板参数 T2,一切都会编译并工作。赋值运算符不允许有多个模板参数吗?
c++ ×12
templates ×10
gcc ×3
c++11 ×2
c ×1
c++-modules ×1
c++03 ×1
c++20 ×1
filesize ×1
function ×1
inline ×1
object-files ×1
optimization ×1
type-traits ×1