性能:基本类型的typedef与包装类? 我想在C++中定义一个新的类型,它只是一些原始类型(在我的例子中是 int /COD>,可以是任何类型)。在本例中,我调用类型NodeId

性能:基本类型的typedef与包装类? 我想在C++中定义一个新的类型,它只是一些原始类型(在我的例子中是 int /COD>,可以是任何类型)。在本例中,我调用类型NodeId,c++,inline,micro-optimization,C++,Inline,Micro Optimization,我可以只使用typedef int NodeId。我需要NodeIds的默认值,因此我将使用#define NULL_NODE_ID-1 现在,我认为最好是定义一个类而不是typedef,以允许函数isValid()和一个默认构造函数构造nullNodeId: class NodeId { int value; public: inline NodeId() : value(-1) {} inline NodeId(int value) : value(value) {}

我可以只使用
typedef int NodeId
。我需要
NodeId
s的默认值,因此我将使用
#define NULL_NODE_ID-1

现在,我认为最好是定义一个类而不是
typedef
,以允许函数
isValid()
和一个默认构造函数构造null
NodeId

class NodeId
{
    int value;
public:
    inline NodeId() : value(-1) {}
    inline NodeId(int value) : value(value) {}
    inline operator int() {return value;}
    inline bool isValid() {return value != -1;}
    //...
};

使用第二种方法是否存在性能缺陷?

如果使用相对较新的编译器,则生成的代码应相同。编译器内联这些成员函数应该没有问题。如果您真的对此有疑问,您可以随时反汇编可执行文件。

如果编译器优化设置允许编译器内联所有内容,那么应该没有性能差异。我能发现的唯一缺点是,基于此类对象的表达式可能不再符合编译时常量的条件,这在模板编程中可能很重要。除此之外,您还可以在无运行时成本的情况下获得额外的清晰度。
另外,您的
运算符int
中缺少
常量
,并且
是有效的

inline operator int() const {return value;}
inline bool isValid() const {return value != -1;}

事实上,有两个原因可以想象这会更慢

首先,无法创建未初始化的NodeId。通常,这是件好事。但假设您有这样的代码:

NodeId nodeid;
foo.initializeNodeId(&nodeid);
你将要做一项实际上不必要的额外任务

您可以通过添加一个特殊的构造函数来解决这个问题。创建一个Foo::createNodeId()可能更好,这样您就不需要Foo::initializeNodeId(&NodeId),但是如果您不控制Foo的定义,这可能是不可能的

其次,NodeId不是编译时常量表达式。正如dasblinkenlight所指出的,这更可能导致代码不合法的问题,而不是导致性能问题,但两者都有可能。(为什么?因为如果使用int,可能会强制编译器在运行时插入代码以执行一些可能在编译时完成的计算。但对于名为NodeId的类来说,这可能不是问题…)

幸运的是,如果您使用的是C++11,那么可以使用constexpr解决这个问题。如果你希望你的代码也是合法的C++03,你可以用宏来处理

另外,正如dasblinkenlight指出的,有两种方法缺少常量

最后,没有理由对类定义中定义的方法编写“内联”;它们已经内在地内联了

总而言之:

#if __cplusplus > 201000L
#define CONSTEXPR_ constexpr
#else
#define CONSTEXPR_
#endif

class NodeId
{
    int value;
public:
    struct Uninitialized {};
    CONSTEXPR_ NodeId() : value(-1) {}
    CONSTEXPR_ NodeId(Uninitialized) {}      
    CONSTEXPR_ NodeId(int value) : value(value) {}
    CONSTEXPR_ operator int() const {return value;}
    CONSTEXPR_ bool isValid() const {return value != -1;}
    //...
};
现在您可以这样做,以避免额外的-1任务的成本

NodeId nodeId(NodeId::Uninitialized());
foo.initializeNodeId(&nodeid);
要合法地将NodeId用作非类型模板参数,请执行以下操作:

myClassTemplate<NodeId(3)> c;

<>如果你打算把它变成一个可以被其他编程语言使用的库(比如,如果你把它写成C++ DLL),那么有一个明确的优点,就是它有一个<代码> TyPufIn < /C>
例如,当您为API编写python包装器或java包装器时,在移植类和类型时不会有太多麻烦。那么你所要担心的就是
int

的位大小如果你使用一个新的编译器,那么这应该和使用int是一样的。@mfontani那么你为什么否决这个问题?这是问题的答案,你应该这样发布。@leemes为什么你认为是我?无关:
const NodeId NullNodeId=-1
#define
更好。不,我不想在构造函数上显式,因为我希望类尽可能地表现为基本类型。“编译时常量表达式”,也许?你对常量的看法是对的,我只是在这里发布了这个示例。这不完全正确
int
是POD类型,但
NodeId
不是。编译器将在使用它的地方生成额外的初始化代码,禁止您在联合中使用它,并且它将以其非PODness“感染”其他类。
NodeId x
应该生成与“intx(-1);”相同的代码,因为如果它是
int
,OP就会使用这种类型。不过,non-POD参数是真的。@DanHulme C++11中的non-aggregatesunions@Prætorian这是真的。我倾向于假设这个问题不是关于C++11的,除非它被标记或明确地说出来。感谢这个伟大的答案:)abarnert,有没有可能支持开箱即用的整数算法<代码>节点ID节点;node++不编译。我必须手动实现
操作符+
。不需要手动实现这些琐碎的操作符,这是可能的吗?@leems:不,不是完全自动的。但是boost.operator可以帮助您明确定义一些,并免费获取其他。如果你正在做很多这样的类,你可以使用CRTP,一个基类,或者一个代码生成器,这样至少你只需要定义所有的操作符一次。或者定义一个“SmartInt”类模板,然后执行类似“struct nodeittag{};typedef SmartInt NodeId;”(当然可以将其封装在宏中)。@abarnert我找到了一个非常简单的方法来支持所有现成的算术运算符:只需定义另一个
运算符int&()
:)@leemes:尝试使用运算符int定义两个不同的int包装类&并尝试所有算术组合。但将其与继承/CRTP/taged模板技巧结合起来就足够了吗?
int x = 3;
x += NodeId(1);