【问题标题】:Initialize instance with integer constant 0 but no other constant value or integer variable用整数常量 0 初始化实例,但没有其他常量值或整数变量
【发布时间】:2012-03-14 05:07:19
【问题描述】:

我想定义一个类,它的实例可以从整数常量零构造、隐式构造或赋值,但不能从任何其他数值常量,也不能从整数类型的变量(即使它的值恰好是运行时为零)。它还应该可以从同一类的其他实例复制构造。只要 g++ 4.6 和 MSVC 2010 都支持(在适当的模式下)C++11 功能,就可以使用它们。

具体来说,给定

class X { /* ... */ };
void fn(X);

这些都应该编译:

X a(0);
X b = 0;
X c; c = 0;
X d = a;
X e; e = a;
fn(0);

但这些不应该:

X f(1);
X g = 1;
X h; h = 1;
fn(1);

int ii = 23;
X a(ii);
X j = ii;
X k; k = ii;
fn(ii);

我试过了,但没用:

class X {
public:
   X() {} 
   constexpr X(int v) { static_assert(v == 0, "must be initialized from zero"); }
};

⟶

test.cc: In constructor ‘constexpr X::X(int)’:
test.cc:3:29: error: non-constant condition for static assertion
test.cc:3:29: error: ‘v’ is not a constant expression

【问题讨论】:

  • 好的。用例是什么?
  • 为什么不使用默认构造函数呢?
  • 这个用例冗长而复杂,并不是那么相关。可以说 X 与数字有一些相似之处(整个类定义了一些清单常量和一些算术运算符),但与数字文字的唯一重叠点是零。默认构造很好,但在某些情况下(例如传递fn 的参数)写零更自然。
  • 我不明白...删除默认构造函数,如果不为0则抛出异常。为什么不起作用?
  • @0A0D 我希望错误初始化是编译错误,而不是运行时错误。

标签: c++


【解决方案1】:

如果需要 C++0x,你可以使用std::nullptr_t:

class X
{
public:
  X () { }
  X (std::nullptr_t) { }
  void operator= (std::nullptr_t) { }
};

当然,缺点是X 也可以用nullptr 初始化。

【讨论】:

  • 如果没有人能想出更好的办法,这可能是可以接受的。
  • 没有特别的理由使用nullptr_t,您也可以定义一个自己的zero 类,它不能转换为其他任何东西。当对象不是指针时使用nullptr 会强烈违反ᴘʀɪɴᴄɪᴘʟᴇ ᴏꜰ ʟᴇᴀꜱᴛ ꜱᴜʀᴘʀɪꜱᴇ。
  • @leftaroundabout 如果我理解您的建议正确,则不允许人们写“X(0)”,这是一项要求。 nullptr_t 具有不寻常的特性,即可以从值为 0 的整数常量隐式转换,但不能从其他整数常量转换;这恰好是我想要的。
  • @Zack:确实,它不允许这样做。我没想到你的想法实际上是使用X(0),因为我认为它不会可靠:用f(0) 调用f(std::nullptr_t); 不符合标准,是吗? f(NULL) 保证可以工作,但 NULL 宏不需要简单的 0。现在可能不相关,但由于 nullptr 现在是推荐的关键字,我希望编译器在将来执行此类函数调用时至少会产生警告消息。
  • C++11(好吧,N3337)4.10p1 中的最后一句话:“整数类型的空指针常量可以转换为 std::nullptr_t 类型的纯右值。”所以是的,将f(std::nullptr_t) 称为f(0) 是符合要求的。我当然希望这样的警告永远不会发生;我强烈认为编写空指针的正确方法是0,或者在非常罕见不起作用的情况下,(TYPE *)0 用于某些具体类型。 (换句话说,我赞成添加nullptr_t,但不赞成添加nullptr 本身。)
【解决方案2】:

您需要将 (v == 0) 替换为常量表达式...

类似

constexpr bool is_zero_construct(size_t number)
{
  return number == 0;
}

constexpr X(int v) { static_assert(is_zero_construct(v), "must be initialized from zero"); }

【讨论】:

  • 没有帮助:v 本身仍然不是 constexpr,因此它反对对 is_zero_construct 的调用。将 int v 更改为 int constexpr v 让我“错误:参数不能被声明为 constexpr”。
【解决方案3】:

您可以利用只有 0 可以隐式转换为指针的事实:

struct X {
  X();
  X(void*);
};

这满足您的要求,但当然也允许使用指针进行初始化。

【讨论】:

  • 在上下文中使用指针意外初始化的可能性比使用非零整数常量意外初始化的可能性要小得多,但如果可能的话,我仍然希望将其排除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
相关资源
最近更新 更多