【问题标题】:Using a variable that was defined in an if statement before使用之前在 if 语句中定义的变量
【发布时间】:2018-01-26 22:56:46
【问题描述】:
int main() {
    if(i = 0) {
        myclass1 a = "Example1";
    }
    else {
        myclass2 a = "Example2";
    }
    cout << a << endl;
}

我知道一种方法是在块外定义它,但如果在检查i 的条件之前我还没有确定a 的类型怎么办?

【问题讨论】:

  • 可能是模板和/或 lambda 的?
  • i = 0 是赋值,而不是比较。
  • myclass1myclass2 共享公共基类吗?
  • 将打印行移入 if 范围?我知道这是一个例子,但是如果用法很短,即使它们相同,也要复制它;而不是使用额外的库和许多杂乱的技巧。
  • 在 stackoverflow 上的一个常见观察是:当提出一个“简单”的问题时,会提出一些非常复杂、棘手或优雅的解决方案。虽然这可能很有趣,很有教育意义,而且不一定是“错误的”,但我认为在很多情况下,更适合问:您真正想要实现什么?乙>。这是必须首先回答的问题,然后才能首先将任何潜在解决方案视为“好”或“坏”。

标签: c++ object


【解决方案1】:

C++ 是一种静态类型语言,需要在编译时知道代码中使用的变量类型。

没有办法编写一个 C++ 程序,其中编译了像 std::cout &lt;&lt; a; 这样的语句,并且直到运行时才知道 a 的类型。

为此,您需要一种动态类型语言,例如 Python 或 JavaScript。

【讨论】:

  • 现在有std::any(或C++17之前的boost::any)。也就是说,如果没有令人信服的用例,我不建议使用它。
  • @ArneVogel:这是一种特殊的类型
  • -1 继承是为了能够做这样的事情。您也没有提到a 超出范围。
  • @6502:很公平,从语言律师的角度来看,std::any 与类型一样是静态的。然而,从实际的角度来看,它是一种动态类型的推动者。 C++ 是一种多范式语言,它允许“完全”静态类型(例如使用模板)、继承多态性甚至对不相关类型的类型擦除(通常使用多态性实现)。例如,std::shared_ptr 能够存储一个类型擦除的删除器,与其他删除器几乎没有共同点(即复制/移动和函数调用(运算符))。
  • @ArneVogel:从实际的角度来看,主要问题是 C++ 缺乏任何形式的运行时自省(除了一个意义不大的类型“名称”),这阻碍了编译操作对象的代码,除非它在编译时知道的特征。它甚至缺乏复制或销毁对象的能力,因为尽管是基于复制的语言,但复制和销毁并未以标准方式(在运行时)公开。你可以尝试粘贴一些棘手的补丁,比如无状态删除器的想法,但你不能在这条路上走得太远。
【解决方案2】:

我自己也有这样的代码,而实际上我正在尝试相同代码的不同变体。然后我意识到最好的选择是使用预处理器#if,它解决了我的问题:

#define VARIATION 2

...

#if VARIATION == 1
    myclass1 a = "Example1";
#else
    myclass2 a = "Example2";
#endif

我知道它可能无法解决您的问题,但至少这是一种解决方法。

【讨论】:

    【解决方案3】:

    如果是这个特定的问题,我认为这会容易得多

    int main(){
        if(i == 0)          //You wrote i=0 !! silly mistake
            std::cout << myclass1("Example1");
        else
            std::cout << myclass2("Example2");
    }
    

    或者你可以选择

    template<class T>
    void foo(T out)
    {
        std::cout << out;
    }
    
    int main()
    {
        if( i==0 )
            foo(myclass1("ex1"));
        else
            foo(myclass2("ex2"));
    }
    

    否则

    this 是要走的路 我建议不要在这里使用 cout,因为它可能没有重载来接受您的用户定义的类。

    【讨论】:

      【解决方案4】:

      这绝对需要多态性,如果您想让它更优雅一点,可以选择使用factory pattern。工厂模式并不神奇,它只是将if 隐藏在一个漂亮的包装器中。

      为什么不采用另一种方法,例如std::variant 基本上是伪装的union?好吧,如果您能够以相同的名称存储不同种类的东西,甚至是 any 种类 (std::any),那就太好了,但它并不是很有用,因为您 em> 想对对象做一些有意义的事情。如果您想做完全不同的、不相关的事情,那么您也可以使用 if 块范围内的不同对象(并且使用完全不同的代码)。但是,如果您想对不同的对象执行相同或相似的操作,那么它们(通常)需要是相同或相关的类型。

      不同类型通常没有相同的数据成员或相同的可公开访问的成员函数。因此,在不同类型的源代码级别上做同样的事情通常是行不通的(除非巧合)。

      但是如果两个类在它们的接口上确实有相同的子集,并且您希望能够以一种或另一种方式互换,那么从基类继承是最自然的,并且惯用的事情。这就是发明多态性的目的。使用惯用的东西。

      (您可以通过模板助手在不同的、不相关的类型上调用具有相同名称的函数获得相同的净效果,并假设您使用的名称存在,这将正常工作,但它的风格几乎没有那么好,并且通过两次实例化函数会导致巨大的膨胀)。

      【讨论】:

        【解决方案5】:

        我会尝试给你一个实际的答案,假设你已经习惯用 JavaScript 或其他东西做这类事情,并且只是尝试用 C++ 编写代码。

        首先,您应该了解在 C++ 中,cout &lt;&lt; a。实际上可以根据a 的类型调用一个完全不同的 方法。因此,当您对该类型一无所知时,写cout &lt;&lt; a 没有任何意义。事实上,你对a 什么都做不了,除非你对 C++ 的类型有足够的了解来决定你想调用哪个方法或运算符。

        如果你的两个类都有一个可接受的公共基础,那么你可以这样做:

        int main() {
            base_class *pa;
            my_class1 a1;
            my_class2 a2;
        
            if(i = 0) {
                a1 = "Example1";
                pa = &a1;
            }
            else {
                a2 = "Example2";
                pa = &a2;
            }
            cout << *pa << endl;
        }
        

        请注意,当您编写cout &lt;&lt; *pa 时,您不一定调用cout &lt;&lt; a 将使用的相同方法。在第一种情况下,您调用的方法知道如何输出base_class 的所有子类,而在第二种情况下,您可能正在调用专门为myclass1myclass2 编写的方法。

        当没有可接受的基类时,我们就不要在 C++ 中编写类似的代码:

        int main() {
            if(i = 0) {
                myclass1 a = "Example1";
                cout << a << endl;
            }
            else {
                myclass2 a = "Example2";
                cout << a << endl;
            }
        }
        

        请记住,在这些情况下调用的两个方法可能是完全不同的方法。这就像打电话给cout.printClass1(a)cout.printClass2(a)。当 C++ 可以根据参数类型确定要调用哪个方法时,您可以为完全不同的方法使用相同的名称。

        当您编写 cout.callWhatever(a) 时,JavaScript 没有任何魔法可以自动在 printClass1printClass2 之间进行选择,C++ 也没有。在两种语言中,如果您必须为myclass1myclass2 调用完全不同的方法,那么您编写不同的调用。

        【讨论】:

        • +1 用于避免 new / delete 同时仍然获得多态性,这与其他一些指向基类的答案不同。
        【解决方案6】:

        你可以试试polymorphism。 假设myclass1myclass2“实现”了一个名为myclass 的类,你可以这样做:

        int main() {
            myclass*a;
            if (i=0) {
                a = new myclass1("Example1");
            } else {
                a = new myclass2("Example2");
            }
            cout<<*a<<endl;
        }
        

        如果您以后想主动使用myclass1myclass2 类型,可以使用dynamic_cast,但根据您的需要以及您在继承类和基类中实现的行为,可能不会有必要。

        注意我在这里使用了一个原始指针,因为它是一个短暂的对象,很明显程序已经结束。我鼓励您阅读有关smart pointers 的信息并适当地使用它们以避免memory leaks。注意某些平台中的内存泄漏会持续到重新启动后,可能需要手动释放(删除)分配的内存。更多关于here.

        【讨论】:

        • 这就是为什么我明确声明我假设 myclass1 和 myclass2 实现了一个公共基类。
        • OP 可以随心所欲,我只是提供一个可以清楚地解决他的问题的建议,如问题中所述(我将其解释为“如何使用变量声明它在 if 条件之外,在条件的每个路径内分别初始化了两种可能的不同类型?”)。
        • @ViệtEngland 您评论说“所以 myclass1 和 myclass2 确实有一个基类 myclass。”这对任何答案都很重要且相关。请编辑您的问题并添加此重要信息。
        • 停止鼓励显式手动动态分配(new 的原始使用)。我们有智能指针。
        • 有存放原始指针的地方;从来没有一个地方可以懒惰不打扰自己清理。
        【解决方案7】:
        int main() {
            auto call = [](auto a) {
                std::cout << a << std::endl;
            };
        
            if(i = 0)
                call(myclass1 { "Example1" });
            else 
                call(myclass2 { "Example2" });
        }
        

        【讨论】:

          【解决方案8】:

          如果您能够使用,您可以使用std::variantstd::any,以防您的类型没有公共基类。这些类是任何或指定类型的类型安全容器。 std::variant 的示例如下:

          #include <iostream>
          #include <string>
          #include <variant>
          
          int main() {
              bool input = false;
              std::cin >> input;
          
              std::variant<int, long, double, std::string> myVariant;
              if(input)
                  myVariant = "Example1";
              else
                  myVariant = 3.14;
          
              std::visit([](auto&& arg) { std::cout << arg << std::endl; }, myVariant);
          }
          

          除了,您还可以使用boost::variantboost::any

          【讨论】:

          • 注意:可能希望使用 lambda 来生成变体,以避免默认构造(毕竟并非所有类型都是默认构造的)。
          • @r1verside 哦,这不是我所说的讽刺 ;-) (但我同意。我不想冒犯提问者,但问题的性质使它非常 很可能这是XY problem 的情况...)
          猜你喜欢
          • 1970-01-01
          • 2023-03-19
          • 1970-01-01
          • 1970-01-01
          • 2011-10-01
          • 2012-10-31
          • 1970-01-01
          相关资源
          最近更新 更多