函数参数内的If语句;分支类型

If statements within function parameters; Branching types

本文关键字:分支 类型 语句 If 参数 函数      更新时间:2023-10-16

我用Clojure(一种函数式语言)编程已经有一段时间了,我必须为一个类使用C++。我一直在尝试使用Clojure中我喜欢的一些功能(例如,高阶函数、lambdas、参数线程、动态类型等),但我遇到了一些困难。

在Clojure中(我认为还有其他函数语言)可以做的一件事是在任何地方使用几乎任何函数。例如,Clojure中的if语句可以插入函数调用的参数/自变量中,但在C++中,这是不可能的。

作为我正在做的更大的事情的一个简化示例,我试图让以下内容发挥作用:

println(if (true) { 100 } else { 200 });

我实现了函数println来接受任意数量的参数;它的工作方式与Clojure的println类似(类似于Java的System.out.println),但使用cout

显然,if语句的语法可能是每个分支后面都必须有一个分号(除了不带括号的较短语法选项)。所以我添加了分号,并将其放入函数println中,但这并不起作用——编译器抛出了一个expected expression错误。

所以我转向了宏。起初,我实现了这个:

#define if1(pred, expr_true, expr_false) 
   if (pred) {                           
     return expr_true;                   
   } else {                              
     return expr_false;                  
   }

我把它放在println中,就像这样:

println(if1(true, 100, 200));

但这导致编译器抛出相同的错误。

因此,我想也许我可以将if语句封装在lambda中(因为在C++中,函数不能在函数调用的参数中声明):

#define if2(pred, expr_true, expr_false) 
  []() -> decltype(auto){                
    if (pred) { return expr_true;  }     
    else      { return expr_false; }}() 

我是这样用的:

println(if2(true, 100, 200));

这一次,显然编译器不再关心if语句处于一个奇怪的位置,它编译得很好,并打印了期望值100

然而,当我尝试println(if2(true, "true!", 200))时,编译器生成了以下错误:

error: 'decltype(auto)' in return type deduced as 'int' here but deduced as 'char const (&)[5]' in earlier return statement

如何避免此错误?

此外,一般来说,考虑到decltype(auto)在这些情况下似乎不起作用,我如何声明返回类型根据分支条件语句的不同可能返回类型而变化?

谢谢你的帮助!

注意:我使用带有参数-Wall -Wextra -pedantic -std=c++1y的g++

您可以使用boost::variant

#include <boost/variant.hpp>
template<typename T, typename U>
auto if2(bool cond, T const& t, U const& u)
{
    typedef boost::variant<T const&, U const&> vari;
    return cond ? vari(t) : vari(u);
}
int main()
{
    println( if2(rand() % 2 == 0, "true", 200) );
}

演示

对我来说,我会使用三元运算符。(<expression>? <value-if-true>: <value-if-false>)

用法:

println("%d", (true? 100: 200));

在普通C++中,您无法真正绕过这个错误,因为重载解析发生在编译时。例如:

void foo(int a) { }
void foo(char const * a) { }
void bar(bool b)
{
    foo(if2(b, "true!", 200));
}

编译器需要知道在编译时调用哪个foo(),如果if2()的结果类型在编译时未知,则无法执行此操作。因此,lambda必须返回一些特定类型,这意味着true表达式和false表达式必须是同一类型。

我建议使用三元运算符?:

void bar(bool b)
{
    foo(b ? "true!" : 200);
}

这仍然无法编译,但您会得到一个更清晰的错误消息:

error: operands to ?: have different types ‘const char*’ and ‘int’

此外,三元表达式可以隐式转换其中一个操作数,使其与另一操作数的类型兼容,而lambda返回类型推导则不那么聪明:

// error: inconsistent types ‘int’ and ‘double’ deduced for lambda return type
auto x = [] () { if (true) { return 0; } else { return 0.0; } };
// This works; y will be a double and the int would be implicitly converted.
auto y = true ? 0 : 0.0;