【问题标题】:Expansion of types from comparison从比较中扩展类型
【发布时间】:2013-03-29 13:54:45
【问题描述】:

我知道下面的代码不起作用,我完全理解为什么。我实际上不明白的是为什么不:

int main(int argc, char *argv[]) {
    std::cout << (atoi(argv[1]) ? "foo" : 'b') << std::end;
}

为什么:当然,这个表达式可能生成字符串或者整数,这就是编译器指出的错误:

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

为什么不:由于operator&lt;&lt; 与const char* 和char 这两种类型都有绑定,为什么编译器不像在模板中那样执行代码扩展-- 我猜这就是执行的操作。

例如,如果我有:

template <class T>
void a(const T& value) {
    std::cout << a << std::endl;
}

我可以调用a("foo") 和a('b'),而且——我猜——编译器会使用类型名称[T = const char*] 对函数进行一次扩展,然后使用[T = char] 进行另一次扩展。

这可能是一个简单的问题,即 C++ 做什么——以及它不做什么——,但我看不到如果执行扩展,是否有任何极端情况会出现错误。

【问题讨论】:

标签: c++ templates compiler-construction typing


【解决方案1】:

C++ 是一种已编译的静态类型语言,必须在编译时知道表达式的类型。表达式atoi(argv[1]) ? "foo" : 'b' 可以是const char* 或char,具体取决于argv[1] 的值,这在编译时是未知的。只有在程序实际执行时才知道这个值。所以当编译器试图把这个表达式转换成机器码时,它不能决定把这个表达式当成哪种类型。

要查看它真的与operator&lt;&lt; 没有任何关系,只需拥有表达式本身即可:

int main(int argc, const char* argv[])
{
  atoi(argv[1]) ? "foo" : 'b';
}

即使是这个won't compile,也会出现以下错误:

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

【讨论】:

    【解决方案2】:

    与cout 或operator &lt;&lt; 无关。表达式

    atoi(argv[1]) ? "foo" : 'b'
    

    本身不会编译。您提供给?: 的第二个和第三个运算符必须是相同的类型,或者可以隐式转换为彼此的类型。

    【讨论】:

    • 我知道这是关于三元运算符的,我只是不明白为什么不能执行扩展,因为operator&lt;&lt; 能够接收这两种类型。当接收数据的方法能够处理这两种类型时,为什么三元运算符不能丢弃每种情况。
    • @Rubens 它不能被执行,因为它是非法的。假设它是一个函数而不是三元运算符。 if (...) return "foo"; else return 'b';。是否应该仅仅因为您将其作为参数提供给cout &lt;&lt; 而允许编译?是否应该更改 ?: 的定义以使其适用于某些极端情况?没有。
    • 嗯...这是我的盲点!感谢您的回答! (:
    【解决方案3】:

    这是你认为你应该要求的:

    #include <iostream>
    #include <utility>
    #include <type_traits>
    #include <functional>
    
    template<typename Left, typename Right>
    auto tri( bool b, Left&& left, Right&& right )
      -> decltype( std::forward<Left>(left)() )
    {
      if (b)
        return std::forward<Left>(left)();
      else
        return std::forward<Right>(right)();
    }
    
    int main(int /*argc*/, char *argv[]) {
      tri(
         atoi(argv[1]),
         []()->std::ostream&{ return std::cout<<"foo"; },
         []()->std::ostream&{ return std::cout<<'b'; }
      ) << std::endl;
    }
    

    但这不是? 所做的。

    可以修改 C++ 来满足您的要求,但类型级联会无限增长。每次您有一个可以返回类型 A 或类型 B 的表达式时,都必须派生调用代码,这可能会导致进一步的派生。

    必须扩展函数的签名以列出它“可能”返回的所有类型。

    现在,虽然这可能是 C++ 未来的一个有价值的特性,但它不是 C++ 现在所做的。 C++ 中的每个表达式都有一个单一的、明确的类型——在模板代码中,当您实例化模板时会发生这种情况。

    顺便说一句,在 C++ 中具有多类型返回值的能力将为您提供类似于异常处理的能力,其中函数可以返回值或错误标志。由于调用代码必须在您调用多类型返回值函数时自动分叉,因此它必须处理该错误标志(通过将其作为替代类型返回,或通过本地处理)。

    【讨论】:

    • @0x499602D2 抱歉——当我过早发布它时,我正在向Tri 添加返回值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-21
    • 2015-02-07
    • 2011-01-01
    • 2021-03-06
    相关资源
    最近更新 更多