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

时间:2014-08-31 03:07:23

标签: c++ types functional-programming

我已经用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 ++。

3 个答案:

答案 0 :(得分:2)

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

用法:

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

答案 1 :(得分:1)

您可以使用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) );
}

Demo

答案 2 :(得分:1)

你无法真正解决vanilla 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;