条件运算符从其参数的副本返回值

Posted

技术标签:

【中文标题】条件运算符从其参数的副本返回值【英文标题】:Conditional operator returns value from a copy of its argument 【发布时间】:2019-05-20 17:42:00 【问题描述】:

当条件运算符c?x:y 的两个返回参数的类型不同时,会在应用强制转换之前进行复制。可以在保持简单易用性的同时防止这种情况吗?

我有这个(因问题而删减):

struct Fixed 
  char data[10];
  Fixed(char *s)  strcpy(data, s); 
  operator char*()  return this->data; 
;

但它对条件运算符和 nullptr 的行为非常糟糕:

Fixed f =...; // just here to show the type of f, don't read too much into this
...
bool condition = ...;
char *s = condition ? nullptr : f;

制作了 f 的副本,并且 s 现在指向堆栈上的一个值,该值将很快消失。这一切都是因为nullptr 的类型是std::nullptr_tf 将通过演员表传递给char*,但只有在它首先被复制之后。这似乎是非常糟糕的行为,但这是规范所说的。

我目前的解决方法是只制作演员和演员explicit,但这有点破坏可用性。还有其他解决方案吗?

这里有一些示例代码可供使用(忽略质量,因为我正在大量使用它以了解 gcc 和 LLVM 如何以不同方式处理此问题):

#include <cstdio>
#include <iostream>
#include <array>
#include <cstring>

using namespace std;

struct A : public array<char,4> 
  A()  cerr<<"def\n"; 
  A(const A &o)  cerr<<"copy\n"; (*this)=o;
  A(const char *s)  cerr<<"ctor\n";assign(s);  // explicit fixes
  void assign(const char*s) cerr<<"assign\n";memset(this->begin(), 0, 4); strncpy(this->begin(), s, 4); 
  operator char*()  cerr<<"cast\n";return this->begin(); 
  //operator void*()  cerr<<"void\n";return this->begin(); 
  //operator std::nullptr_t()  cerr<<"void\n";return (std::nullptr_t)this->begin(); 
;

volatile A *faraway = new A();

char* plain(A &v)  cerr<<"in pl\n";
  return faraway == nullptr ? nullptr : v;

char* cast1(A &v)  cerr<<"in c1\n";
  return faraway == nullptr ? (char*)nullptr : v;

char* cast2(A &v)  cerr<<"in c2\n";
  return faraway == nullptr ? nullptr : (char*)v;


int main() 
  A *a = new A; a->assign("asd");

  char *x = a->data();
  cerr << "\nplain\n";
  char *yp = plain(*a);
  cerr << "\nc1\n";
  char *y1 = cast1(*a);
  cerr << "\nc2\n";
  char *y2 = cast2(*a);

  cerr << "\n---\n";
  cerr << (void*)a << "\n" << (void*)(a->data()) << "\n" << (void*)x << "\n---\n";
  cerr << (void*)yp << "\n" << (void*)y1 << "\n" << (void*)y2 << "\n";

  return 0;

【问题讨论】:

这里发生了很多奇怪的事情cond?nullptr:(char*)f 工作正常。 cond?(char*)nullptr:f 在 LLVM 中工作正常,但 GCC 拒绝编译(我明确指定了 Fixed(char*) ctor)。 @JohnKugelman 是的,基本上。我只是在那里展示f的类型。该结构是从哈希中检索的,因此我尝试在哈希中返回结构的char*,而不是返回堆栈上结构副本的char* 使用somefunc(A &amp;v)然后&amp;v不能为nullptr。请展示一个更好的例子或更好的设计。 @Ripi2:令人惊讶的是,这不是真的。你可以做somefunc(*(A*)nullptr),只要不做左值到右值的转换是合法的。 @BenVoigt 给我一个惊喜,是的。无论如何,我认为 OP 想要使用 char *x = somefunc(someinstance) ,其中 someinstance 不是指针,而是类实例。 【参考方案1】:

您的问题是三元的操作数有std::nullptr_tstruct Fixed 类型。推理规则查找从一种操作数类型到另一种的转换,以及常见的基类。没有机会推断char*

您可以通过提供operator std::nullptr_t() = delete 自动发现错误;

【讨论】:

但它似乎确实推断出char*。它甚至调用了正确的operator char* 方法。正是这种奇怪的需要首先创建一个临时对象(以及 GCC 需要 Fixed(char*) ctor 但实际上并未使用它的奇怪行为)。 @JasonN:它不推断char*,三元计算结果为struct Fixed 临时对象。然后在char* 变量的初始化期间调用operator char*()。您可以通过在两者之间引入auto-typed 变量来证明这一点,例如auto t = condition ? nullptr : f; cout &lt;&lt; "Between ternary and char* initialization"; char* s = t; 我认为三元,必须从两个分支返回相同的类型,在这种情况下,它需要在三元之前进行扣除(即,我不能返回 A,除非如果可以从nullptr 转换为A)。 ...我刚刚使用auto 进行了测试,并强制编译器错误以查看类型。三元返回一个A,这也意味着可能存在从nullptrA 的隐式转换?我不明白这怎么可能? 为了增加更多的丑陋,如果比较为真,nullptr 将转换为 A 段错误,没有编译器警告。为什么甚至可以将 nullptr_t 转换为任何非指针类型?再玩一点,我实际上可以让编译器承认它可以从nullptr_t 转换为A 和/viseversa/。丑陋,但我想不会比其他 C++ 丑陋差多少。耸肩 你认为这是一个 GCC 错误,需要 A(char*) 编译,不像 LLVM,并且从不使用它?我应该举报吗?

以上是关于条件运算符从其参数的副本返回值的主要内容,如果未能解决你的问题,请参考以下文章

参数值中的 msbuild 任务条件运算符

JavaScript三目运算符的使用

js 三目运算

在 Spring bean 有条件地设置属性值时,SpEL 条件运算符未按预期进行评估(使用 XML 配置)

java中if判断中,3个条件满足怎么写

条件语句