为什么MATLAB会抛出一个;输出参数太多“;重载subsref(下标引用)时出错?

为什么MATLAB会抛出一个;输出参数太多“;重载subsref(下标引用)时出错?,matlab,operator-overloading,Matlab,Operator Overloading,作为一个玩具示例,我有一个类,它简单地将一个向量或矩阵包装在一个对象中,并包含一个创建它的时间戳。我正在尝试重载subsrefsubsref,以便 ()引用的工作原理与标准向量和矩阵类型完全相同 {}引用的工作方式与()引用的工作方式完全相同(换句话说,与单元格无关) 引用允许我访问对象的私有属性和其他非技术属性的字段 代码: 但是,大括号{}引用不起作用。我运行这个代码 clear all x = TimeStampValue(magic(3)); x{1:2} 我得到了这个错误: Erro

作为一个玩具示例,我有一个类,它简单地将一个向量或矩阵包装在一个对象中,并包含一个创建它的时间戳。我正在尝试重载subsref
subsref
,以便

  • ()
    引用的工作原理与标准向量和矩阵类型完全相同
  • {}
    引用的工作方式与
    ()
    引用的工作方式完全相同(换句话说,与单元格无关)
  • 引用允许我访问对象的私有属性和其他非技术属性的字段 代码:

    但是,大括号
    {}
    引用不起作用。我运行这个代码

    clear all
    x = TimeStampValue(magic(3));
    x{1:2}
    
    我得到了这个错误:

    Error using TimeStampValue/subsref
    Too many output arguments.
    Error in main (line 3)
    x{1:2} 
    
    meexception.last
    向我提供以下信息:

    identifier: 'MATLAB:maxlhs'
       message: 'Too many output arguments.'
         cause: {0x1 cell}
         stack: [1x1 struct]
    
    这是没有帮助的,因为异常堆栈中唯一的东西是包含我在上面运行的三行代码的文件

    我在
    subsref
    中的switch语句的第一行放置了一个断点,但MATLAB从未到达它


    这是怎么回事?
    ()
    引用都能像您预期的那样工作,那么为什么
    {}
    引用不能工作呢?

    当重载花括号
    {}
    以返回与通常不同数量的输出参数时,还需要重载
    numel
    以返回预期的数字(在本例中为1)更新:R2015b开始,新函数被创建为重载函数,而不是
    numel
    。问题仍然是一样的,但是这个函数应该重载,而不是像我在下面的原始答案中描述的那样
    numel
    。另请参见本页。摘录:

    当类重载
    numArgumentsFromSubscript
    时,MATLAB调用此方法而不是
    numel
    ,以计算
    subsref
    nargout
    subsasgn
    nargin
    所需的参数数

    如果类没有重载
    numArgumentsFromSubscript
    ,MATLAB调用
    numel
    来计算
    nargout
    nargin
    的值

    下面将对基本问题进行更多解释(需要指定输出参数的数量)


    原始答案(对于R2015b+,使用
    numArgumentsFromSubscript
    而不是
    numel

    为了处理使用大括号编制索引时输出参数列表以逗号分隔的可能性,MATLAB调用
    numel
    ,根据输入索引的大小确定输出参数的数量(根据)。如果重载的
    subsref
    定义中的输出参数数量与
    numel
    提供的数量不一致(即小于),则会出现“输出参数过多”错误。如MathWorks所述:

    因此,要允许在返回大量与输入大小不一致的参数时将大括号索引到对象中,需要在类目录中重载NUMEL函数

    由于
    x{1:2}
    通常提供两个输出(
    x{1},x{2}
    ),因此定义
    函数x=subsref(B,S)
    与此输入不兼容。解决方案是在类中包含一个简单的
    numel
    方法来重载内置函数,如下所示:

    函数n=numel(varargin)
    n=1;
    结束
    
    现在,
    {}
    索引按预期工作,模仿
    ()

    然而,以这种方式重载花括号是“我们[MathWorks]不希望客户编写的特定类型的代码”。MathWorks建议:

    如果将类设计为只输出一个参数,则不建议使用要求重载NUMEL的大括号索引。相反,建议您使用平滑大括号()索引

    更新:有趣的是:

    在MATLAB发布R2015b之前,对于返回或分配给逗号分隔列表的某些索引表达式,MATLAB错误地计算了
    subsref
    输出和
    subsasgn
    输入的预期参数数

    在R2015b版本中,MATLAB根据索引表达式所需的参数数量正确计算
    nargout
    nargin
    的值

    那么,也许这一问题现在已经解决了



    想到的另一种解决方案是将
    函数x=subsref(B,S)
    更改为
    函数varargout=subsref(B,S)
    ,并添加
    varargout=cell(1,numel(B));varargout{1}=x。正如Amro在评论中所指出的,预先分配单元格对于避免未分配参数的错误是必要的。

    我刚刚遇到了同样的问题。更糟糕的是,输出参数的数量必须等于
    numel()
    返回的值,这不仅适用于大括号
    {}
    ,也适用于点
    操作

    这意味着,如果覆盖
    numel()
    以返回通常的
    prod(size(obj))
    ,则无法访问基础对象的任何属性(如上例中的
    x.time
    ),因为
    subsref()
    将返回多个输出

    但如果
    numel()
    只返回1,则它与
    prod(size(obj))
    不匹配,这是大多数使用数值或基于
    restrape()
    的代码所期望的。事实上,MATLAB编辑器的气球帮助立即表明“NUMEL(x)通常比PROD(SIZE(x))快”,这表明它们是等效的,但显然不是

    一个可能的解决方案是使
    numel()
    返回
    prod(size(obj))
    并显式写入
    identifier: 'MATLAB:maxlhs'
       message: 'Too many output arguments.'
         cause: {0x1 cell}
         stack: [1x1 struct]
    
    >> clear all % needed to reset the class definition
    >> x = TimeStampValue(magic(3));
    >> x(1:2)
    ans = 
        7.355996e+05
         8     3
    >> x{1:2}
    ans = 
        7.355996e+05
         8     3
    
    x.get_time()
    
    x.matrix(1,:)
    
    m = x.get_matrix();
    m(1,:)
    
    classdef TestClass < handle
        methods
    
            function n = numel(~,varargin)
                n = 1;
            end
    
            function varargout = subsref(input,S)
                varargout = builtin('subsref',input,S);
            end
    
            function out = twoOutputs(~)
                out = {}; out{1} = 2; out{2} = 3; 
            end
        end
    end
    
    >> testClass = TestClass();
    >> [a,b] = testClass.twoOutouts()
    a =
    
         2
    
    b =
    
         3
    
    function [R,varargout] = subsref(P,S) 
    
    varargout(1:nargout-1) = cell(1,nargout-1);