【问题标题】:How to differentiate implicit/explicit constructor calls in C++?如何区分 C++ 中的隐式/显式构造函数调用?
【发布时间】:2018-02-24 22:46:13
【问题描述】:

我正在尝试检测何时调用了显式构造函数调用与隐式调用。

假设我们有一个类Foo

class Foo{
public:
    Foo(int _val) : val(_val){};
private:
    int val;
}

void bar(Foo f){
   ...
}

我们可以这样调用bar:

Foo f(10);
bar(f);

或喜欢:

bar(10); // implicit initialization

现在,我知道如果我们明确 ctor:

class Foo{
public:
    explicit Foo(int _val) : val(_val){};
private:
    int val;
}

那么我们可以得到这个错误:

bar(10); // ERROR! implicit initialization not allowed.

所以我认为也许有一种解决方法来检测显式调用与隐式调用,如下所示:

class Foo{
public:
    explicit Foo(int _val) : val(_val){}; // calling Foo f(10); bar(f);
    Foo(int _val) : val(_val){}; // calling bar(10);
private:
    int val;
}

但正如预期的那样,它返回“不能重载”,因为函数签名不明确。

最终结果应该是这样的:

class Foo{
public:
    explicit Foo(int _val) : val(_val), flag(true) {}; // calling Foo f(10); bar(f);
    Foo(int _val) : val(_val), flag(false) {}; // calling bar(10);
private:
    int val;
    bool flag;
}

void bar(Foo f){
    std::cout << "f's flag set to : " << f.flag << std::endl;
}

Foo f(10);
bar(f); // f's flag set to : 1
bar(10); // f's flag set to : 0

但显然,由于上述尝试是徒劳的,而且我没有更好的主意,我想知道是否可以在 C++ 中做到这一点。如果不是,那也没关系。

【问题讨论】:

  • 你认为你为什么需要这样做?
  • 这听起来很像XY problem。如果您需要检测到这一点,则说明您的模型存在很大问题。
  • 我不是在问是否应该这样做,我是在问是否可以这样做。这不是我到达我想要的地方的必要路径。
  • 您当前的情况下,您可以区分,因为显式构造函数也会进行复制,而隐式构造函数不会进行额外的复制。
  • 顺便说一句,在您的评论中,“flag set to 1”放错了位置,应该是f(10)

标签: c++ class oop constructor explicit


【解决方案1】:

甚至可以在 C++ 中做到这一点?

没有

正如您所见,重载歧义是一个问题,这使您无法知道调用的是隐式构造函数还是显式构造函数。

人们可能会假设 在您的情况下,通过留意显式构造函数所需的副本(与隐式构造函数相比),可以知道调用了哪个构造函数,但这不是很可靠,因为好的编译器可以利用Copy Elision 并绕过复制操作。

因此,如果我们依靠我们的复制假设来确定是否调用了显式构造函数,我们可能会收到一个假阴性,以防复制实际上被忽略了。

一般来说,复制构造函数是允许省略的,它们应该有副作用。

【讨论】:

  • 好答案,我对复制 elison 的论点深信不疑:) 我赞成。
【解决方案2】:

所以我认为也许有一种解决方法来检测显式调用与隐式调用,如下所示:

不,这是不可能的。如果这纯粹是出于好奇,那么您就有答案了。如果您正在尝试解决一个真正的问题,您可能想要发布真正的问题。

【讨论】:

    【解决方案3】:

    这很简单,但解决方案是在被调用的函数中,而不是在您正在创建的对象中。

    void bar(Foo&) { ... }
    void bar(Foo&&) { ... }
    
    Foo f(10);
    bar(f);  // calls the first one
    bar(10); // calls the second one
    

    【讨论】:

    • bar(Foo(42)); 调用第二个。
    • @Jarod42 -- 是的,但这不是问题中的示例之一。 这是一个固有的模糊问题,因为没有说明它的真正目标。其中一个 cmets 表明,实际目标是捕获右值,而不是隐式构造,在这种情况下,这种方法确实解决了这个问题。
    • 其实也不错;这很有趣,因为这是我的“后备计划”。我打算用右值和引用重载函数。但是,这确实有 2^n 个重载的缺点,其中 n 是 Foo 的输入数。对于普通的二元/一元运算符,并不太糟糕。赞成读我的想法
    • @OneRaynyDay -- 确实;我们通过标准库重载了右值和左值。
    • @PeteBecker 哇,你写的标准库?很荣幸能在你面前:P
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-02
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 2016-06-07
    相关资源
    最近更新 更多